/*-
 * Copyright (c) 1999 MAEKAWA Masahide <bishop@rr.iij4u.or.jp>,
 *              Nick Hibma <n_hibma@FreeBSD.org>
 * All rights reserved.
 * Copyright (c) <2014-2015>, <Huawei Technologies Co., Ltd>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *    $FreeBSD: releng/11.2/sys/dev/usb/storage/umass.c 331722 2018-03-29 02:50:57Z eadler $
 *    $NetBSD: umass.c,v 1.28 2000/04/02 23:46:53 augustss Exp $
 */

/* Also already merged from NetBSD:
 *    $NetBSD: umass.c,v 1.67 2001/11/25 19:05:22 augustss Exp $
 *    $NetBSD: umass.c,v 1.90 2002/11/04 19:17:33 pooka Exp $
 *    $NetBSD: umass.c,v 1.108 2003/11/07 17:03:25 wiz Exp $
 *    $NetBSD: umass.c,v 1.109 2003/12/04 13:57:31 keihan Exp $
 */

/*
 * Universal Serial Bus Mass Storage Class specs:
 * http://www.usb.org/developers/devclass_docs/usb_msc_overview_1.2.pdf
 * http://www.usb.org/developers/devclass_docs/usbmassbulk_10.pdf
 * http://www.usb.org/developers/devclass_docs/usb_msc_cbi_1.1.pdf
 * http://www.usb.org/developers/devclass_docs/usbmass-ufi10.pdf
 */

/*
 * Ported to NetBSD by Lennart Augustsson <augustss@NetBSD.org>.
 * Parts of the code written by Jason R. Thorpe <thorpej@shagadelic.org>.
 */

/*
 * The driver handles 3 Wire Protocols
 * - Command/Bulk/Interrupt (CBI)
 * - Command/Bulk/Interrupt with Command Completion Interrupt (CBI with CCI)
 * - Mass Storage Bulk-Only (BBB)
 *   (BBB refers Bulk/Bulk/Bulk for Command/Data/Status phases)
 *
 * Over these wire protocols it handles the following command protocols
 * - SCSI
 * - UFI (floppy command set)
 * - 8070i (ATAPI)
 *
 * UFI and 8070i (ATAPI) are transformed versions of the SCSI command set. The
 * sc->sc_transform method is used to convert the commands into the appropriate
 * format (if at all necessary). For example, UFI requires all commands to be
 * 12 bytes in length amongst other things.
 *
 * The source code below is marked and can be split into a number of pieces
 * (in this order):
 *
 * - probe/attach/detach
 * - generic transfer routines
 * - BBB
 * - CBI
 * - CBI_I (in addition to functions from CBI)
 * - CAM (Common Access Method)
 * - SCSI
 * - UFI
 * - 8070i (ATAPI)
 *
 * The protocols are implemented using a state machine, for the transfers as
 * well as for the resets. The state machine is contained in umass_t_*_callback.
 * The state machine is started through either umass_command_start() or
 * umass_reset().
 *
 * The reason for doing this is a) CAM performs a lot better this way and b) it
 * avoids using tsleep from interrupt context (for example after a failed
 * transfer).
 */

/*
 * The SCSI related part of this driver has been derived from the
 * dev/ppbus/vpo.c driver, by Nicolas Souchu (nsouch@FreeBSD.org).
 *
 * The CAM layer uses so called actions which are messages sent to the host
 * adapter for completion. The actions come in through umass_cam_action. The
 * appropriate block of routines is called depending on the transport protocol
 * in use. When the transfer has finished, these routines call
 * umass_cam_cb again to complete the CAM command.
 */
/*----------------------------------------------------------------------------
 * Notice of Export Control Law
 * ===============================================
 * Huawei LiteOS may be subject to applicable export control laws and regulations, which might
 * include those applicable to Huawei LiteOS of U.S. and the country in which you are located.
 * Import, export and usage of Huawei LiteOS in any manner by you shall be in compliance with such
 * applicable export control laws and regulations.
 *---------------------------------------------------------------------------*/

/*lint -esym(459,scsi_inquiry,scsi_read_capacity,scsi_request_sense,
scsi_test_unit_ready,umass_bbb_config,umass_cbi_config,
p_umsf,g_dev_umass_ops,p_umsf,scsi_read_capacity_16)*/

/*lint -esym(459,dev_quantity,g_umass_dev_array,umass_taskid)*/

#ifdef USB_GLOBAL_INCLUDE_FILE
#include USB_GLOBAL_INCLUDE_FILE
#include "scsi_all.h"
#include "driver.h"
#include "disk.h"
#endif
#include "scsi.h"
#include "umass_fs.h"
#if USB_HAVE_DEVICE_TOPOLOGY
#include "implementation/usb_btree.h"
#endif

#ifdef LOSCFG_USB_DEBUG
#define    DIF(m, x)                \
  do {                        \
    if (umass_debug & (m)) { x ; }        \
  } while (0)

#define    DPRINTF_UMASS(sc, m, fmt, ...)            \
  do {                            \
    if (umass_debug & (m)) {                \
        PRINTK("%s:%s: " fmt,                \
           (sc) ? (const char *)(sc)->sc_name :    \
           (const char *)"umassX",            \
        __FUNCTION__ ,## __VA_ARGS__);        \
    }                            \
  } while (0)

#define    UDMASS_GEN    0x00010000    /* general */
#define    UDMASS_SCSI    0x00020000    /* scsi */
#define    UDMASS_UFI    0x00040000    /* ufi command set */
#define    UDMASS_ATAPI    0x00080000    /* 8070i command set */
#define    UDMASS_CMD    (UDMASS_SCSI|UDMASS_UFI|UDMASS_ATAPI)
#define    UDMASS_USB    0x00100000    /* USB general */
#define    UDMASS_BBB    0x00200000    /* Bulk-Only transfers */
#define    UDMASS_CBI    0x00400000    /* CBI transfers */
#define    UDMASS_WIRE    (UDMASS_BBB|UDMASS_CBI)
#define    UDMASS_ALL    0xffff0000    /* all of the above */
static int umass_debug = 0;//UDMASS_ALL;
static int umass_throttle;

void umass_debug_func(int level)
{
    switch(level) {
        case 0:
            umass_debug = 0;
            PRINTK("Close the umass debug\n");
            break;
        case UDMASS_GEN:
        case UDMASS_SCSI:
        case UDMASS_UFI:
        case UDMASS_ATAPI:
        case UDMASS_CMD:
        case UDMASS_USB:
        case UDMASS_BBB:
        case UDMASS_CBI:
        case UDMASS_WIRE:
        case UDMASS_ALL:
            umass_debug = level;
            PRINTK("The level of umass debug is %x\n", level);
            break;
        default:
            PRINT_ERR("The level of umass debug is invalid, please refer to umass.c\n");
            break;
    }
}
DEBUG_MODULE(umass, umass_debug_func);
#else
#define    DIF(...) do { } while (0)
#define    DPRINTF_UMASS(...) do { } while (0)
#endif

#define BUS_PROBE_GENERIC  0

#define    UMASS_BULK_SIZE (1 << 17)
#define    UMASS_CBI_DIAGNOSTIC_CMDLEN 12    /* bytes */
#define    UMASS_MAX_CMDLEN MAX(12, CBWCDBLENGTH)    /* bytes */

/* USB transfer definitions */

#define    UMASS_T_BBB_RESET1      0    /* Bulk-Only */
#define    UMASS_T_BBB_RESET2      1
#define    UMASS_T_BBB_RESET3      2
#define    UMASS_T_BBB_COMMAND     3
#define    UMASS_T_BBB_DATA_READ   4
#define    UMASS_T_BBB_DATA_RD_CS  5
#define    UMASS_T_BBB_DATA_WRITE  6
#define    UMASS_T_BBB_DATA_WR_CS  7
#define    UMASS_T_BBB_STATUS      8
#define    UMASS_T_BBB_MAX         9

#define    UMASS_T_CBI_RESET1      0    /* CBI */
#define    UMASS_T_CBI_RESET2      1
#define    UMASS_T_CBI_RESET3      2
#define    UMASS_T_CBI_COMMAND     3
#define    UMASS_T_CBI_DATA_READ   4
#define    UMASS_T_CBI_DATA_RD_CS  5
#define    UMASS_T_CBI_DATA_WRITE  6
#define    UMASS_T_CBI_DATA_WR_CS  7
#define    UMASS_T_CBI_STATUS      8
#define    UMASS_T_CBI_RESET4      9
#define    UMASS_T_CBI_MAX        10

#define    UMASS_T_MAX MAX(UMASS_T_CBI_MAX, UMASS_T_BBB_MAX)

/* Generic definitions */

/* Direction for transfer */
#define    DIR_NONE    0
#define    DIR_IN        1
#define    DIR_OUT        2

/* device name */
#define    DEVNAME        "umass"
#define    DEVNAME_SIM    "umass-sim"

/* Approximate maximum transfer speeds (assumes 33% overhead). */
#define    UMASS_FULL_TRANSFER_SPEED    1000
#define    UMASS_HIGH_TRANSFER_SPEED    40000
#define    UMASS_SUPER_TRANSFER_SPEED    400000
#define    UMASS_FLOPPY_TRANSFER_SPEED    20

#define    UMASS_TIMEOUT            20000    /* ms */

/* CAM specific definitions */
#define    UMASS_SCSIID_MAX    1    /* maximum number of drives expected */
#define    UMASS_SCSIID_HOST    UMASS_SCSIID_MAX

/* Bulk-Only features */
#define    UR_BBB_RESET        0xff    /* Bulk-Only reset */
#define    UR_BBB_GET_MAX_LUN    0xfe    /* Get maximum lun */

/*
 * SCSI I/O Request CCB used for the XPT_SCSI_IO and XPT_CONT_TARGET_IO
 * function codes.
 */
struct ccb_scsiio {
    uint8_t *data_ptr;        /* Ptr to the data buf/SG list */
    uint32_t dxfer_len;        /* Data transfer length */
    uint32_t resid;        /* Transfer residual length: 2's comp */
    int32_t  status;

    struct     scsi_sense_data sense_data;
    uint8_t   sense_len;        /* Number of bytes to autosense */
};

union ccb {
    struct    ccb_scsiio        csio;
};


/* Command Block Wrapper */
typedef struct {
    uDWord    dCBWSignature;
#define    CBWSIGNATURE    0x43425355
    uDWord    dCBWTag;
    uDWord    dCBWDataTransferLength;
    uByte    bCBWFlags;
#define    CBWFLAGS_OUT    0x00
#define    CBWFLAGS_IN    0x80
    uByte    bCBWLUN;
    uByte    bCDBLength;
#define    CBWCDBLENGTH    16
    uByte    CBWCDB[CBWCDBLENGTH];
} __packed umass_bbb_cbw_t;

#define    UMASS_BBB_CBW_SIZE    31

/* Command Status Wrapper */
typedef struct {
    uDWord    dCSWSignature;
#define    CSWSIGNATURE    0x53425355
#define    CSWSIGNATURE_IMAGINATION_DBX1    0x43425355
#define    CSWSIGNATURE_OLYMPUS_C1    0x55425355
    uDWord    dCSWTag;
    uDWord    dCSWDataResidue;
    uByte    bCSWStatus;
#define    CSWSTATUS_GOOD    0x0
#define    CSWSTATUS_FAILED    0x1
#define    CSWSTATUS_PHASE    0x2
} __packed umass_bbb_csw_t;

#define    UMASS_BBB_CSW_SIZE    13

/* CBI features */

#define    UR_CBI_ADSC    0x00

typedef union {
    struct {
        uint8_t    type;
#define    IDB_TYPE_CCI        0x00
        uint8_t    value;
#define    IDB_VALUE_PASS        0x00
#define    IDB_VALUE_FAIL        0x01
#define    IDB_VALUE_PHASE        0x02
#define    IDB_VALUE_PERSISTENT    0x03
#define    IDB_VALUE_STATUS_MASK    0x03
    } __packed common;

    struct {
        uint8_t    asc;
        uint8_t    ascq;
    } __packed ufi;
} __packed umass_cbi_sbl_t;

struct umass_info {
    uint32_t sectorsize;
    uint64_t sectornum;
};

struct umass_softc;            /* see below */

typedef void (umass_callback_t)(struct umass_softc *sc, union ccb *ccb,
        uint32_t residue, uint8_t status);

#define    STATUS_CMD_OK        0    /* everything ok */
#define    STATUS_CMD_UNKNOWN    1    /* will have to fetch sense */
#define    STATUS_CMD_FAILED    2    /* transfer was ok, command failed */
#define    STATUS_WIRE_FAILED    3    /* couldn't even get command across */

typedef uint8_t (umass_transform_t)(struct umass_softc *sc, uint8_t *cmd_ptr,
        uint8_t cmd_len);

/* Wire and command protocol */
#define    UMASS_PROTO_BBB        0x0001    /* USB wire protocol */
#define    UMASS_PROTO_CBI        0x0002
#define    UMASS_PROTO_CBI_I    0x0004
#define    UMASS_PROTO_WIRE    0x00ff    /* USB wire protocol mask */
#define    UMASS_PROTO_SCSI    0x0100    /* command protocol */
#define    UMASS_PROTO_ATAPI    0x0200
#define    UMASS_PROTO_UFI        0x0400
#define    UMASS_PROTO_RBC        0x0800
#define    UMASS_PROTO_COMMAND    0xff00    /* command protocol mask */

/* Device specific quirks */
#define    NO_QUIRKS        0x0000
    /*
     * The drive does not support Test Unit Ready. Convert to Start Unit
     */
#define    NO_TEST_UNIT_READY    0x0001
    /*
     * The drive does not reset the Unit Attention state after REQUEST
     * SENSE has been sent. The INQUIRY command does not reset the UA
     * either, and so CAM runs in circles trying to retrieve the initial
     * INQUIRY data.
     */
#define    RS_NO_CLEAR_UA        0x0002
    /* The drive does not support START STOP.  */
#define    NO_START_STOP        0x0004
    /* Don't ask for full inquiry data (255b).  */
#define    FORCE_SHORT_INQUIRY    0x0008
    /* Needs to be initialised the Shuttle way */
#define    SHUTTLE_INIT        0x0010
    /* Drive needs to be switched to alternate iface 1 */
#define    ALT_IFACE_1        0x0020
    /* Drive does not do 1Mb/s, but just floppy speeds (20kb/s) */
#define    FLOPPY_SPEED        0x0040
    /* The device can't count and gets the residue of transfers wrong */
#define    IGNORE_RESIDUE        0x0080
    /* No GetMaxLun call */
#define    NO_GETMAXLUN        0x0100
    /* The device uses a weird CSWSIGNATURE. */
#define    WRONG_CSWSIG        0x0200
    /* Device cannot handle INQUIRY so fake a generic response */
#define    NO_INQUIRY        0x0400
    /* Device cannot handle INQUIRY EVPD, return CHECK CONDITION */
#define    NO_INQUIRY_EVPD        0x0800
    /* Pad all RBC requests to 12 bytes. */
#define    RBC_PAD_TO_12        0x1000
    /*
     * Device reports number of sectors from READ_CAPACITY, not max
     * sector number.
     */
#define    READ_CAPACITY_OFFBY1    0x2000
    /*
     * Device cannot handle a SCSI synchronize cache command.  Normally
     * this quirk would be handled in the cam layer, but for IDE bridges
     * we need to associate the quirk with the bridge and not the
     * underlying disk device.  This is handled by faking a success
     * result.
     */
#define    NO_SYNCHRONIZE_CACHE    0x4000
    /* Device does not support 'PREVENT/ALLOW MEDIUM REMOVAL'. */
#define    NO_PREVENT_ALLOW    0x8000

#if USB_HAVE_DEVICE_TOPOLOGY
/*lint -esym(459,hub_tree)*/
extern usbd_btTree hub_tree;
#endif

struct umass_softc {
    union ccb *data_ccb;
    struct scsi_sense cam_scsi_sense;
    struct scsi_test_unit_ready cam_scsi_test_unit_ready;
    struct mtx sc_mtx;
    EVENT_CB_S sc_event;
    struct {
        uint8_t *data_ptr;
        union ccb *ccb;
        umass_callback_t *callback;

        uint32_t data_len;    /* bytes */
        uint32_t data_rem;    /* bytes */
        uint32_t data_timeout;    /* ms */
        uint32_t actlen;    /* bytes */

        uint8_t    cmd_data[UMASS_MAX_CMDLEN]; /*lint !e506*/
        uint8_t    cmd_len;    /* bytes */
        uint8_t    dir;
        uint8_t    lun;
    }    sc_transfer;

    struct umass_info info;

    /* Bulk specific variables for transfers in progress */
    umass_bbb_cbw_t cbw;        /* command block wrapper */
    umass_bbb_csw_t csw;        /* command status wrapper */

    /* CBI specific variables for transfers in progress */
    umass_cbi_sbl_t sbl;        /* status block */

    device_t sc_dev;
    struct usb_device *sc_udev;
    //struct cam_sim *sc_sim;        /* SCSI Interface Module */
    struct usb_xfer *sc_xfer[UMASS_T_MAX]; /*lint !e506*/

    /*
     * The command transform function is used to convert the SCSI
     * commands into their derivatives, like UFI, ATAPI, and friends.
     */
    umass_transform_t *sc_transform;

    uint32_t sc_unit;
    uint32_t sc_quirks;        /* they got it almost right */
    uint32_t sc_proto;        /* wire and cmd protocol */

    uint8_t    sc_name[16];
    uint8_t    sc_iface_no;        /* interface number */
    uint8_t    sc_maxlun;        /* maximum LUN number, inclusive */
    uint8_t    sc_last_xfer_index;
    uint8_t    sc_status_try;
    BOOL    sc_detach_status;
    BOOL    sc_super_disk;    /* TRUE: Disk is bigger than 2T; FALSE: Disk is less than 2T */
    struct mtx sc_umass_mtx; /* The mtx is used to prevent data read and write competition */
};

struct umass_probe_proto {
    uint32_t quirks;
    uint32_t proto;

    int    error;
};

#if USB_SUPPORT_SD_HOT_PLUG
struct umass_dev_info {
    struct umass_softc *sc;
    unsigned int dev_unit;
    int used; /* 0: not use; 1: in use */
    int attached; /* 0: not attach; 1: in attach */
    struct mtx dev_mtx; /* The mtx is used to prevent U disk insertion or extraction competition */
};

static struct umass_dev_info g_umass_dev_array[MAX_DEVICE] = {0};
static void umass_task_check(int flag);
static void umass_dev_delete(struct umass_softc *sc, unsigned int dev_unit);
int umass_dev_is_attached(unsigned int dev_unit);
static void umass_dev_attach_flag_set(int dev_unit);
pthread_t   umass_taskid;
#define umass_dev_mtx_init(id, type)  (void)mtx_init(&g_umass_dev_array[id].dev_mtx, NULL, NULL, type)
#define umass_dev_mtx_destroy(id)  (void)mtx_destroy(&g_umass_dev_array[id].dev_mtx)
#define umass_dev_lock(id)  (void)mtx_lock(&g_umass_dev_array[id].dev_mtx)
#define umass_dev_unlock(id)    (void)mtx_unlock(&g_umass_dev_array[id].dev_mtx)
#else
#define umass_dev_lock(id)  (void)mtx_lock(NULL)
#define umass_dev_unlock(id)    (void)mtx_unlock(NULL)
#endif

struct umass_softc *p_umsf = (struct umass_softc *)NULL;
/* prototypes */

static device_probe_t umass_probe;
static device_attach_t umass_attach;
static device_detach_t umass_detach;

static usb_callback_t umass_tr_error;
static usb_callback_t umass_t_bbb_reset1_callback;
static usb_callback_t umass_t_bbb_reset2_callback;
static usb_callback_t umass_t_bbb_reset3_callback;
static usb_callback_t umass_t_bbb_command_callback;
static usb_callback_t umass_t_bbb_data_read_callback;
static usb_callback_t umass_t_bbb_data_rd_cs_callback;
static usb_callback_t umass_t_bbb_data_write_callback;
static usb_callback_t umass_t_bbb_data_wr_cs_callback;
static usb_callback_t umass_t_bbb_status_callback;
static usb_callback_t umass_t_cbi_reset1_callback;
static usb_callback_t umass_t_cbi_reset2_callback;
static usb_callback_t umass_t_cbi_reset3_callback;
static usb_callback_t umass_t_cbi_reset4_callback;
static usb_callback_t umass_t_cbi_command_callback;
static usb_callback_t umass_t_cbi_data_read_callback;
static usb_callback_t umass_t_cbi_data_rd_cs_callback;
static usb_callback_t umass_t_cbi_data_write_callback;
static usb_callback_t umass_t_cbi_data_wr_cs_callback;
static usb_callback_t umass_t_cbi_status_callback;

static void    umass_cancel_ccb(struct umass_softc *);
static void    umass_init_shuttle(struct umass_softc *);
static void    umass_t_bbb_data_clear_stall_callback(struct usb_xfer *,
            uint8_t, uint8_t, usb_error_t);
static int    umass_command_start(struct umass_softc *, uint8_t, void *,
            uint32_t, uint32_t, umass_callback_t *, union ccb *);
static uint8_t    umass_bbb_get_max_lun(struct umass_softc *);
static void    umass_cbi_start_status(struct umass_softc *);
static void    umass_t_cbi_data_clear_stall_callback(struct usb_xfer *,
            uint8_t, uint8_t, usb_error_t);
static void    umass_cam_cb(struct umass_softc *, union ccb *, uint32_t, uint8_t);
static uint8_t    umass_scsi_transform(struct umass_softc *, uint8_t *, uint8_t);
static uint8_t    umass_rbc_transform(struct umass_softc *, uint8_t *, uint8_t);
static uint8_t    umass_ufi_transform(struct umass_softc *, uint8_t *, uint8_t);
static uint8_t    umass_atapi_transform(struct umass_softc *, uint8_t *,
            uint8_t);
static uint8_t    umass_no_transform(struct umass_softc *, uint8_t *, uint8_t);

#ifdef LOSCFG_USB_DEBUG
static void    umass_bbb_dump_cbw(struct umass_softc *, umass_bbb_cbw_t *);
static void    umass_bbb_dump_csw(struct umass_softc *, umass_bbb_csw_t *);
static void    umass_cbi_dump_cmd(struct umass_softc *, void *, uint8_t);
#endif

static void devunit_to_devname(unsigned int dev_unit, char *devname);
static int32_t umass_attach_dev(struct umass_softc *sc, unsigned int dev_unit);
static void umass_detach_dev_sub(struct umass_softc *sc, int dev_unit, int flag);

static struct usb_config umass_bbb_config[UMASS_T_BBB_MAX] = { /*lint -e26 -e546*/

    [UMASS_T_BBB_RESET1] = {
        .type = UE_CONTROL,
        .endpoint = 0x00,    /* Control pipe */
        .direction = UE_DIR_ANY,
        .bufsize = sizeof(struct usb_device_request),
        .callback = &umass_t_bbb_reset1_callback,
        .timeout = 5000,    /* 5 seconds */
        .interval = 500,    /* 500 milliseconds */
    },

    [UMASS_T_BBB_RESET2] = {
        .type = UE_CONTROL,
        .endpoint = 0x00,    /* Control pipe */
        .direction = UE_DIR_ANY,
        .bufsize = sizeof(struct usb_device_request),
        .callback = &umass_t_bbb_reset2_callback,
        .timeout = 5000,    /* 5 seconds */
        .interval = 50,    /* 50 milliseconds */
    },

    [UMASS_T_BBB_RESET3] = {
        .type = UE_CONTROL,
        .endpoint = 0x00,    /* Control pipe */
        .direction = UE_DIR_ANY,
        .bufsize = sizeof(struct usb_device_request),
        .callback = &umass_t_bbb_reset3_callback,
        .timeout = 5000,    /* 5 seconds */
        .interval = 50,    /* 50 milliseconds */
    },

    [UMASS_T_BBB_COMMAND] = {
        .type = UE_BULK,
        .endpoint = UE_ADDR_ANY,
        .direction = UE_DIR_OUT,
        .bufsize = sizeof(umass_bbb_cbw_t),
        .callback = &umass_t_bbb_command_callback,
        .timeout = 5000,    /* 5 seconds */
    },

    [UMASS_T_BBB_DATA_READ] = {
        .type = UE_BULK,
        .endpoint = UE_ADDR_ANY,
        .direction = UE_DIR_IN,
        .bufsize = UMASS_BULK_SIZE,
        .flags = {.proxy_buffer = 1,.short_xfer_ok = 1,.ext_buffer=1,},
        .callback = &umass_t_bbb_data_read_callback,
        .timeout = 0,    /* overwritten later */
    },

    [UMASS_T_BBB_DATA_RD_CS] = {
        .type = UE_CONTROL,
        .endpoint = 0x00,    /* Control pipe */
        .direction = UE_DIR_ANY,
        .bufsize = sizeof(struct usb_device_request),
        .callback = &umass_t_bbb_data_rd_cs_callback,
        .timeout = 5000,    /* 5 seconds */
    },

    [UMASS_T_BBB_DATA_WRITE] = {
        .type = UE_BULK,
        .endpoint = UE_ADDR_ANY,
        .direction = UE_DIR_OUT,
        .bufsize = UMASS_BULK_SIZE,
        .flags = {.proxy_buffer = 1,.short_xfer_ok = 1,.ext_buffer=1,},
        .callback = &umass_t_bbb_data_write_callback,
        .timeout = 0,    /* overwritten later */
    },

    [UMASS_T_BBB_DATA_WR_CS] = {
        .type = UE_CONTROL,
        .endpoint = 0x00,    /* Control pipe */
        .direction = UE_DIR_ANY,
        .bufsize = sizeof(struct usb_device_request),
        .callback = &umass_t_bbb_data_wr_cs_callback,
        .timeout = 5000,    /* 5 seconds */
    },

    [UMASS_T_BBB_STATUS] = {
        .type = UE_BULK,
        .endpoint = UE_ADDR_ANY,
        .direction = UE_DIR_IN,
        .bufsize = sizeof(umass_bbb_csw_t),
        .flags = {.short_xfer_ok = 1,},
        .callback = &umass_t_bbb_status_callback,
        .timeout = 5000,    /* ms */
    },
};

static struct usb_config umass_cbi_config[UMASS_T_CBI_MAX] = { /*lint -e26*/

    [UMASS_T_CBI_RESET1] = {
        .type = UE_CONTROL,
        .endpoint = 0x00,    /* Control pipe */
        .direction = UE_DIR_ANY,
        .bufsize = (sizeof(struct usb_device_request) +
            UMASS_CBI_DIAGNOSTIC_CMDLEN),
        .callback = &umass_t_cbi_reset1_callback,
        .timeout = 5000,    /* 5 seconds */
        .interval = 500,    /* 500 milliseconds */
    },

    [UMASS_T_CBI_RESET2] = {
        .type = UE_CONTROL,
        .endpoint = 0x00,    /* Control pipe */
        .direction = UE_DIR_ANY,
        .bufsize = sizeof(struct usb_device_request),
        .callback = &umass_t_cbi_reset2_callback,
        .timeout = 5000,    /* 5 seconds */
        .interval = 50,    /* 50 milliseconds */
    },

    [UMASS_T_CBI_RESET3] = {
        .type = UE_CONTROL,
        .endpoint = 0x00,    /* Control pipe */
        .direction = UE_DIR_ANY,
        .bufsize = sizeof(struct usb_device_request),
        .callback = &umass_t_cbi_reset3_callback,
        .timeout = 5000,    /* 5 seconds */
        .interval = 50,    /* 50 milliseconds */
    },

    [UMASS_T_CBI_COMMAND] = {
        .type = UE_CONTROL,
        .endpoint = 0x00,    /* Control pipe */
        .direction = UE_DIR_ANY,
        .bufsize = (sizeof(struct usb_device_request) +
            UMASS_MAX_CMDLEN),
        .callback = &umass_t_cbi_command_callback,
        .timeout = 5000,    /* 5 seconds */
    },

    [UMASS_T_CBI_DATA_READ] = {
        .type = UE_BULK,
        .endpoint = UE_ADDR_ANY,
        .direction = UE_DIR_IN,
        .bufsize = UMASS_BULK_SIZE,
        .flags = {.proxy_buffer = 1,.short_xfer_ok = 1,.ext_buffer=1,},
        .callback = &umass_t_cbi_data_read_callback,
        .timeout = 0,    /* overwritten later */
    },

    [UMASS_T_CBI_DATA_RD_CS] = {
        .type = UE_CONTROL,
        .endpoint = 0x00,    /* Control pipe */
        .direction = UE_DIR_ANY,
        .bufsize = sizeof(struct usb_device_request),
        .callback = &umass_t_cbi_data_rd_cs_callback,
        .timeout = 5000,    /* 5 seconds */
    },

    [UMASS_T_CBI_DATA_WRITE] = {
        .type = UE_BULK,
        .endpoint = UE_ADDR_ANY,
        .direction = UE_DIR_OUT,
        .bufsize = UMASS_BULK_SIZE,
        .flags = {.proxy_buffer = 1,.short_xfer_ok = 1,.ext_buffer=1,},
        .callback = &umass_t_cbi_data_write_callback,
        .timeout = 0,    /* overwritten later */
    },

    [UMASS_T_CBI_DATA_WR_CS] = {
        .type = UE_CONTROL,
        .endpoint = 0x00,    /* Control pipe */
        .direction = UE_DIR_ANY,
        .bufsize = sizeof(struct usb_device_request),
        .callback = &umass_t_cbi_data_wr_cs_callback,
        .timeout = 5000,    /* 5 seconds */
    },

    [UMASS_T_CBI_STATUS] = {
        .type = UE_INTERRUPT,
        .endpoint = UE_ADDR_ANY,
        .direction = UE_DIR_IN,
        .flags = {.short_xfer_ok = 1,.no_pipe_ok = 1,},
        .bufsize = sizeof(umass_cbi_sbl_t),
        .callback = &umass_t_cbi_status_callback,
        .timeout = 5000,    /* ms */
    },

    [UMASS_T_CBI_RESET4] = {
        .type = UE_CONTROL,
        .endpoint = 0x00,    /* Control pipe */
        .direction = UE_DIR_ANY,
        .bufsize = sizeof(struct usb_device_request),
        .callback = &umass_t_cbi_reset4_callback,
        .timeout = 5000,    /* ms */
    },
}; /*lint +e546*/

#define    UFI_COMMAND_LENGTH    12    /* UFI commands are always 12 bytes */
#define    ATAPI_COMMAND_LENGTH    12    /* ATAPI commands are always 12 bytes */

static devclass_t umass_devclass;

static device_method_t umass_methods[] = { /*lint -e611 -e546*/
    /* Device interface */
    DEVMETHOD(device_probe, umass_probe),
    DEVMETHOD(device_attach, umass_attach),
    DEVMETHOD(device_detach, umass_detach),

    DEVMETHOD_END
}; /*lint +e546*/

static driver_t umass_driver = {
    .name = "umass",
    .methods = umass_methods,
    .size = sizeof(struct umass_softc),
};

#if USB_HAVE_DEVICE_TOPOLOGY
UINT64 dev_quantity = 0;
#endif

DRIVER_MODULE(umass, uhub, umass_driver, umass_devclass, NULL, 0); /*lint !e19*/

static uint16_t
umass_get_proto(struct usb_interface *iface)
{
    struct usb_interface_descriptor *id;
    uint16_t retval;

    retval = 0;

    /* Check for a standards compliant device */
    id = usbd_get_interface_descriptor(iface);
    if ((id == NULL) ||
        (id->bInterfaceClass != UICLASS_MASS)) {
        goto done;
    }
    switch (id->bInterfaceSubClass) {
    case UISUBCLASS_SCSI:
        retval |= UMASS_PROTO_SCSI;
        break;
    case UISUBCLASS_UFI:
        retval |= UMASS_PROTO_UFI;
        break;
    case UISUBCLASS_RBC:
        retval |= UMASS_PROTO_RBC;
        break;
    case UISUBCLASS_SFF8020I:
    case UISUBCLASS_SFF8070I:
        retval |= UMASS_PROTO_ATAPI;
        break;
    default:
        goto done;
    }

    switch (id->bInterfaceProtocol) {
    case UIPROTO_MASS_CBI:
        retval |= UMASS_PROTO_CBI;
        break;
    case UIPROTO_MASS_CBI_I:
        retval |= UMASS_PROTO_CBI_I;
        break;
    case UIPROTO_MASS_BBB_OLD:
    case UIPROTO_MASS_BBB:
        retval |= UMASS_PROTO_BBB;
        break;
    default:
        goto done;
    }
done:
    return (retval);
}

/*
 * Match the device we are seeing with the devices supported.
 */
static struct umass_probe_proto
umass_probe_proto(device_t dev, struct usb_attach_arg *uaa)
{
    struct umass_probe_proto ret;
    uint32_t quirks = NO_QUIRKS;
    uint32_t proto = umass_get_proto(uaa->iface);

    (void)memset_s(&ret, sizeof(ret), 0, sizeof(ret));
    ret.error = BUS_PROBE_GENERIC;

    /* Search for protocol enforcement */

    if (usb_test_quirk(uaa, UQ_MSC_FORCE_WIRE_BBB)) {
        proto &= ~UMASS_PROTO_WIRE;
        proto |= UMASS_PROTO_BBB;
    } else if (usb_test_quirk(uaa, UQ_MSC_FORCE_WIRE_CBI)) {
        proto &= ~UMASS_PROTO_WIRE;
        proto |= UMASS_PROTO_CBI;
    } else if (usb_test_quirk(uaa, UQ_MSC_FORCE_WIRE_CBI_I)) {
        proto &= ~UMASS_PROTO_WIRE;
        proto |= UMASS_PROTO_CBI_I;
    }

    if (usb_test_quirk(uaa, UQ_MSC_FORCE_PROTO_SCSI)) {
        proto &= ~UMASS_PROTO_COMMAND;
        proto |= UMASS_PROTO_SCSI;
    } else if (usb_test_quirk(uaa, UQ_MSC_FORCE_PROTO_ATAPI)) {
        proto &= ~UMASS_PROTO_COMMAND;
        proto |= UMASS_PROTO_ATAPI;
    } else if (usb_test_quirk(uaa, UQ_MSC_FORCE_PROTO_UFI)) {
        proto &= ~UMASS_PROTO_COMMAND;
        proto |= UMASS_PROTO_UFI;
    } else if (usb_test_quirk(uaa, UQ_MSC_FORCE_PROTO_RBC)) {
        proto &= ~UMASS_PROTO_COMMAND;
        proto |= UMASS_PROTO_RBC;
    }

    /* Check if the protocol is invalid */

    if ((proto & UMASS_PROTO_COMMAND) == 0) {
        ret.error = ENXIO;
        goto done;
    }

    if ((proto & UMASS_PROTO_WIRE) == 0) {
        ret.error = ENXIO;
        goto done;
    }

    /* Search for quirks */

    if (usb_test_quirk(uaa, UQ_MSC_NO_TEST_UNIT_READY))
        quirks |= NO_TEST_UNIT_READY;
    if (usb_test_quirk(uaa, UQ_MSC_NO_RS_CLEAR_UA))
        quirks |= RS_NO_CLEAR_UA;
    if (usb_test_quirk(uaa, UQ_MSC_NO_START_STOP))
        quirks |= NO_START_STOP;
    if (usb_test_quirk(uaa, UQ_MSC_NO_GETMAXLUN))
        quirks |= NO_GETMAXLUN;
    if (usb_test_quirk(uaa, UQ_MSC_NO_INQUIRY))
        quirks |= NO_INQUIRY;
    if (usb_test_quirk(uaa, UQ_MSC_NO_INQUIRY_EVPD))
        quirks |= NO_INQUIRY_EVPD;
    if (usb_test_quirk(uaa, UQ_MSC_NO_PREVENT_ALLOW))
        quirks |= NO_PREVENT_ALLOW;
    if (usb_test_quirk(uaa, UQ_MSC_NO_SYNC_CACHE))
        quirks |= NO_SYNCHRONIZE_CACHE;
    if (usb_test_quirk(uaa, UQ_MSC_SHUTTLE_INIT))
        quirks |= SHUTTLE_INIT;
    if (usb_test_quirk(uaa, UQ_MSC_ALT_IFACE_1))
        quirks |= ALT_IFACE_1;
    if (usb_test_quirk(uaa, UQ_MSC_FLOPPY_SPEED))
        quirks |= FLOPPY_SPEED;
    if (usb_test_quirk(uaa, UQ_MSC_IGNORE_RESIDUE))
        quirks |= IGNORE_RESIDUE;
    if (usb_test_quirk(uaa, UQ_MSC_WRONG_CSWSIG))
        quirks |= WRONG_CSWSIG;
    if (usb_test_quirk(uaa, UQ_MSC_RBC_PAD_TO_12))
        quirks |= RBC_PAD_TO_12;
    if (usb_test_quirk(uaa, UQ_MSC_READ_CAP_OFFBY1))
        quirks |= READ_CAPACITY_OFFBY1;
    if (usb_test_quirk(uaa, UQ_MSC_FORCE_SHORT_INQ))
        quirks |= FORCE_SHORT_INQUIRY;

done:
    ret.quirks = quirks;
    ret.proto = proto;
    return (ret);
}

static int
umass_probe(device_t dev)
{
    struct usb_attach_arg *uaa =
        (struct usb_attach_arg *)device_get_ivars(dev);
    struct umass_probe_proto temp;

    if (uaa->usb_mode != USB_MODE_HOST) {
        return (ENXIO);
    }
    temp = umass_probe_proto(dev, uaa);

    return (temp.error);
}

static int
umass_attach(device_t dev)
{
    struct umass_softc *sc =
        (struct umass_softc *)device_get_softc(dev);
    struct usb_attach_arg *uaa =
        (struct usb_attach_arg *)device_get_ivars(dev);
    struct umass_probe_proto temp = umass_probe_proto(dev, uaa);
    struct usb_interface_descriptor *id;
    usb_error_t err;

    /*
     * NOTE: the softc struct is cleared in device_set_driver.
     * We can safely call umass_detach without specifically
     * initializing the struct.
     */

    sc->sc_dev = dev;
    sc->sc_udev = uaa->device;
    sc->sc_proto = temp.proto;
    sc->sc_quirks = temp.quirks;
    sc->sc_unit = device_get_unit(dev);
    sc->data_ccb = (union ccb *)NULL;
    sc->sc_detach_status = FALSE;
    sc->sc_super_disk = FALSE;

#if USB_HAVE_DEVICE_TOPOLOGY
    dev_quantity |= 1ull << device_get_unit(dev);
#endif

    (void)snprintf_s((char *)sc->sc_name, sizeof(sc->sc_name), sizeof(sc->sc_name) - 1,
        "%s", device_get_nameunit(dev));

    device_set_usb_desc(dev);

    mtx_init(&sc->sc_mtx, device_get_nameunit(dev),
        NULL, MTX_DEF | MTX_RECURSE);
    mtx_init(&sc->sc_umass_mtx, device_get_nameunit(dev),
            NULL, MTX_DEF | MTX_RECURSE);

    (void)LOS_EventInit(&sc->sc_event);

    /* get interface index */

    id = usbd_get_interface_descriptor(uaa->iface);
    if (id == NULL) {
        device_printf(dev, "failed to get "
            "interface number\n");
        goto detach;
    }
    sc->sc_iface_no = id->bInterfaceNumber;

    device_printf(dev, " ");

    switch (sc->sc_proto & UMASS_PROTO_COMMAND) {
    case UMASS_PROTO_SCSI:
        PRINTK("SCSI");
        break;
    case UMASS_PROTO_ATAPI:
        PRINTK("8070i (ATAPI)");
        break;
    case UMASS_PROTO_UFI:
        PRINTK("UFI");
        break;
    case UMASS_PROTO_RBC:
        PRINTK("RBC");
        break;
    default:
        PRINTK("(unknown 0x%02x)",
            sc->sc_proto & UMASS_PROTO_COMMAND);
        break;
    }

    PRINTK(" over ");

    switch (sc->sc_proto & UMASS_PROTO_WIRE) {
    case UMASS_PROTO_BBB:
        PRINTK("Bulk-Only");
        break;
    case UMASS_PROTO_CBI:        /* uses Comand/Bulk pipes */
        PRINTK("CBI");
        break;
    case UMASS_PROTO_CBI_I:    /* uses Comand/Bulk/Interrupt pipes */
        PRINTK("CBI with CCI");
        break;
    default:
        PRINTK("(unknown 0x%02x)",
            sc->sc_proto & UMASS_PROTO_WIRE);
    }

    PRINTK("; quirks = 0x%04x\n", sc->sc_quirks);

    if (sc->sc_quirks & ALT_IFACE_1) {
        err = usbd_set_alt_interface_index
            (uaa->device, uaa->info.bIfaceIndex, 1);

        if (err) {
            DPRINTF_UMASS(sc, UDMASS_USB, "could not switch to "
                "Alt Interface 1\n");
            goto detach;
        }
    }
    /* allocate all required USB transfers */

    if (sc->sc_proto & UMASS_PROTO_BBB) {

        err = usbd_transfer_setup(uaa->device,
            &uaa->info.bIfaceIndex, sc->sc_xfer, umass_bbb_config,
            UMASS_T_BBB_MAX, sc, &sc->sc_mtx);

        /* skip reset first time */
        sc->sc_last_xfer_index = UMASS_T_BBB_COMMAND;

    } else if (sc->sc_proto & (UMASS_PROTO_CBI | UMASS_PROTO_CBI_I)) {

        err = usbd_transfer_setup(uaa->device,
            &uaa->info.bIfaceIndex, sc->sc_xfer, umass_cbi_config,
            UMASS_T_CBI_MAX, sc, &sc->sc_mtx);

        /* skip reset first time */
        sc->sc_last_xfer_index = UMASS_T_CBI_COMMAND;

    } else {
        err = USB_ERR_INVAL;
    }

    if (err) {
        device_printf(dev, "could not setup required "
            "transfers, %s\n", usbd_errstr(err));
        goto detach;
    }
#ifdef LOSCFG_USB_DEBUG
    if (umass_throttle > 0) {
        uint8_t x;
        int iv;

        iv = umass_throttle;

        if (iv < 1)
            iv = 1;
        else if (iv > 8000)
            iv = 8000;

        for (x = 0; x != UMASS_T_MAX; x++) {
            if (sc->sc_xfer[x] != NULL)
                usbd_xfer_set_interval(sc->sc_xfer[x], iv);
        }
    }
#endif
    /*lint -e546*/
    sc->sc_transform =
        (sc->sc_proto & UMASS_PROTO_SCSI) ? &umass_scsi_transform :
        (sc->sc_proto & UMASS_PROTO_UFI) ? &umass_ufi_transform :
        (sc->sc_proto & UMASS_PROTO_ATAPI) ? &umass_atapi_transform :
        (sc->sc_proto & UMASS_PROTO_RBC) ? &umass_rbc_transform :
        &umass_no_transform;/*lint +e546*/

    /* from here onwards the device can be used. */

    if (sc->sc_quirks & SHUTTLE_INIT) {
        umass_init_shuttle(sc);
    }
    /* get the maximum LUN supported by the device */

    if (((sc->sc_proto & UMASS_PROTO_WIRE) == UMASS_PROTO_BBB) &&
        !(sc->sc_quirks & NO_GETMAXLUN))
        sc->sc_maxlun = umass_bbb_get_max_lun(sc);
    else
        sc->sc_maxlun = 0;

    /* Prepare the SCSI command block */
    sc->cam_scsi_sense.opcode = REQUEST_SENSE;
    sc->cam_scsi_test_unit_ready.opcode = TEST_UNIT_READY;

#define SOFT_CACHE_SIZE 0x40
    sc->data_ccb = (union ccb *)malloc(sizeof(union ccb));
    if (sc->data_ccb == NULL)
        goto detach;
    sc->data_ccb->csio.data_ptr = (uint8_t *)memalign(USB_CACHE_ALIGN_SIZE, SKB_DATA_ALIGN(SOFT_CACHE_SIZE));
    if (sc->data_ccb->csio.data_ptr == NULL)
        goto detach;
    sc->data_ccb->csio.dxfer_len = SOFT_CACHE_SIZE;

    DPRINTF_UMASS(sc, UDMASS_GEN, "Attach finished\n");

    /* register the device*/
    if (umass_attach_dev(sc, device_get_unit(dev)))
    {
        goto detach;
    }

    p_umsf = sc;
    return (0);            /* success */

detach:
    (void)umass_detach(dev);
    return (ENXIO);            /* failure */
}

static int
umass_detach(device_t dev)
{
    struct umass_softc *sc = (struct umass_softc *)device_get_softc(dev);
    unsigned int dev_unit = device_get_unit(dev);

    DPRINTF_UMASS(sc, UDMASS_USB, "\n");

    sc->sc_detach_status = TRUE;

    /* teardown our statemachine */
    usbd_transfer_unsetup(sc->sc_xfer, UMASS_T_MAX); /*lint !e506*/

    mtx_lock(&sc->sc_mtx);

    /* cancel any leftover CCB's */
    umass_cancel_ccb(sc);

    mtx_lock(&sc->sc_umass_mtx);
    if (sc->data_ccb != NULL)
    {
        if (sc->data_ccb->csio.data_ptr != NULL)
        {
            free((void*)sc->data_ccb->csio.data_ptr);
            sc->data_ccb->csio.data_ptr = (uint8_t *)NULL;
        }
        free(sc->data_ccb);
        sc->data_ccb = (union ccb *)NULL;
    }
    mtx_unlock(&sc->sc_umass_mtx);

    umass_detach_dev_sub(sc, dev_unit, 0);
#if USB_SUPPORT_SD_HOT_PLUG
    umass_task_check(1);
#endif

    mtx_unlock(&sc->sc_mtx);
    sc->sc_detach_status = FALSE;
    mtx_destroy(&sc->sc_mtx);
    mtx_destroy(&sc->sc_umass_mtx);

    p_umsf = (struct umass_softc *)NULL;
    return (0);            /* success */
}

static void
umass_init_shuttle(struct umass_softc *sc)
{
    struct usb_device_request req;
    usb_error_t err;
    uint8_t status[2] = {0, 0};

    /*
     * The Linux driver does this, but no one can tell us what the
     * command does.
     */
    req.bmRequestType = UT_READ_VENDOR_DEVICE;
    req.bRequest = 1;        /* XXX unknown command */
    USETW(req.wValue, 0); /*lint !e572*/
    req.wIndex[0] = sc->sc_iface_no;
    req.wIndex[1] = 0;
    USETW(req.wLength, sizeof(status)); /*lint !e572*/
    err = usbd_do_request(sc->sc_udev, (struct mtx *)NULL, &req, &status); /*lint !e545*/
    if (err)
        DPRINTF_UMASS(sc, UDMASS_GEN, "request failed in %s %d, err=%d\n",
        __FUNCTION__, __LINE__, err);

    DPRINTF_UMASS(sc, UDMASS_GEN, "Shuttle init returned 0x%02x%02x\n",
        status[0], status[1]);
}

/*
 * Generic functions to handle transfers
 */

static void
umass_transfer_start(struct umass_softc *sc, uint8_t xfer_index)
{
    DPRINTF_UMASS(sc, UDMASS_GEN, "transfer index = "
        "%d\n", xfer_index);

    if (sc->sc_xfer[xfer_index]) {
        sc->sc_last_xfer_index = xfer_index;
        usbd_transfer_start(sc->sc_xfer[xfer_index]);
    } else {
        umass_cancel_ccb(sc);
    }
}

static void
umass_cancel_ccb(struct umass_softc *sc)
{
    union ccb *umass_ccb = NULL;

    mtx_assert(&sc->sc_mtx, MA_OWNED);

    umass_ccb = sc->sc_transfer.ccb;
    sc->sc_transfer.ccb = (union ccb *)NULL;
    sc->sc_last_xfer_index = 0;

    if (umass_ccb != NULL) {
        (sc->sc_transfer.callback)
            (sc, umass_ccb, (sc->sc_transfer.data_len -
            sc->sc_transfer.actlen), STATUS_WIRE_FAILED);
    }
}

static void
umass_tr_error(struct usb_xfer *xfer, usb_error_t error)
{
    struct umass_softc *sc = (struct umass_softc *)usbd_xfer_softc(xfer);

    if (error != USB_ERR_CANCELLED) {

        DPRINTF_UMASS(sc, UDMASS_GEN, "transfer error, %s -> "
            "reset\n", usbd_errstr(error));
    }
    umass_cancel_ccb(sc);
}

/*
 *return 0: find the corresponding LUN;
 *       1: find the SBC Direct-access;
 *      -1: did not find the LUN.
 */
static int umass_scsi_inquiry_data(struct umass_softc *sc, void *data, int len) /*lint -e438 -e550*/
{
    struct scsiresp_inquiry_s *cur_i = NULL;
    uint8_t pdt;
    uint8_t rmb;
    int is_dir;
    char *name = NULL;

    if (len != SCSIRESP_INQUIRY_SIZEOF)
        return (-1);

    is_dir = 0;
    cur_i = (struct scsiresp_inquiry_s *)data;

    pdt = SCSI_GET_INQUIRY_PDT(cur_i->qualtype);
    rmb = SCSI_GET_INQUIRY_RMB(cur_i->flags1);
    switch (pdt)
    {
        case T_DIRECT:
            name = "SBC Direct-access";
            is_dir = 1;
            break;
        case T_CDROM:
            name = "CD-ROM";
            break;
        case T_OPTICAL:
            name = "Optical memory";
            break;
        case T_RBC:
            name = "RBC Direct-access";
            break;
        default:
            name = "PDT out of scope";
            break;
    }
    DPRINTF_UMASS(sc, UDMASS_BBB, "SCSI: LUN-%d %s %s\n", sc->sc_transfer.lun, name,
            (rmb ? "Removable" : "Not Removable"));

    (void)name;
    (void)rmb; // this is for clearing warning
    return (is_dir);
}/*lint +e438 +e550*/

/*
 * BBB protocol specific functions
 */

static void
umass_t_bbb_reset1_callback(struct usb_xfer *xfer, usb_error_t error)
{
    struct umass_softc *sc = (struct umass_softc *)usbd_xfer_softc(xfer);
    struct usb_device_request req;
    struct usb_page_cache *pc;

    switch (USB_GET_STATE(xfer)) {
    case USB_ST_TRANSFERRED:
        umass_transfer_start(sc, UMASS_T_BBB_RESET2);
        return;

    case USB_ST_SETUP:
        /*
         * Reset recovery (5.3.4 in Universal Serial Bus Mass Storage Class)
         *
         * For Reset Recovery the host shall issue in the following order:
         * a) a Bulk-Only Mass Storage Reset
         * b) a Clear Feature HALT to the Bulk-In endpoint
         * c) a Clear Feature HALT to the Bulk-Out endpoint
         *
         * This is done in 3 steps, using 3 transfers:
         * UMASS_T_BBB_RESET1
         * UMASS_T_BBB_RESET2
         * UMASS_T_BBB_RESET3
         */

        DPRINTF_UMASS(sc, UDMASS_BBB, "BBB reset!\n");

        req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
        req.bRequest = UR_BBB_RESET;    /* bulk only reset */
        USETW(req.wValue, 0); /*lint !e572*/
        req.wIndex[0] = sc->sc_iface_no;
        req.wIndex[1] = 0;
        USETW(req.wLength, 0); /*lint !e572*/

        pc = usbd_xfer_get_frame(xfer, 0);
        usbd_copy_in(pc, 0, &req, sizeof(req));

        usbd_xfer_set_frame_len(xfer, 0, sizeof(req));
        usbd_xfer_set_frames(xfer, 1);
        usbd_transfer_submit(xfer);
        return;

    default:            /* Error */
        umass_tr_error(xfer, error);
        return;
    }
}

static void
umass_t_bbb_reset2_callback(struct usb_xfer *xfer, usb_error_t error)
{
    umass_t_bbb_data_clear_stall_callback(xfer, UMASS_T_BBB_RESET3,
        UMASS_T_BBB_DATA_READ, error);
}

static void
umass_t_bbb_reset3_callback(struct usb_xfer *xfer, usb_error_t error)
{
    umass_t_bbb_data_clear_stall_callback(xfer, UMASS_T_BBB_COMMAND,
        UMASS_T_BBB_DATA_WRITE, error);
}

static void
umass_t_bbb_data_clear_stall_callback(struct usb_xfer *xfer,
    uint8_t next_xfer, uint8_t stall_xfer, usb_error_t error)
{
    struct umass_softc *sc = (struct umass_softc *)usbd_xfer_softc(xfer);

    switch (USB_GET_STATE(xfer)) {
    case USB_ST_TRANSFERRED:
tr_transferred:
        umass_transfer_start(sc, next_xfer);
        return;

    case USB_ST_SETUP:
        if (usbd_clear_stall_callback(xfer, sc->sc_xfer[stall_xfer])) {
            goto tr_transferred;
        }
        return;

    default:            /* Error */
        umass_tr_error(xfer, error);
        return;
    }
}

static void
umass_t_bbb_command_callback(struct usb_xfer *xfer, usb_error_t error)
{
    struct umass_softc *sc = (struct umass_softc *)usbd_xfer_softc(xfer);
    struct usb_page_cache *pc;
    uint32_t tag;
    int ret;

    switch (USB_GET_STATE(xfer)) {
    case USB_ST_TRANSFERRED:
        umass_transfer_start
            (sc, ((sc->sc_transfer.dir == DIR_IN) ? UMASS_T_BBB_DATA_READ :
            (sc->sc_transfer.dir == DIR_OUT) ? UMASS_T_BBB_DATA_WRITE :
            UMASS_T_BBB_STATUS));
        return;

    case USB_ST_SETUP:

        sc->sc_status_try = 0;

        /*
             * the initial value is not important,
             * as long as the values are unique:
             */
        tag = UGETDW(sc->cbw.dCBWTag) + 1;

        USETDW(sc->cbw.dCBWSignature, CBWSIGNATURE);
        USETDW(sc->cbw.dCBWTag, tag);

        /*
             * dCBWDataTransferLength:
             *   This field indicates the number of bytes of data that the host
             *   intends to transfer on the IN or OUT Bulk endpoint(as indicated by
             *   the Direction bit) during the execution of this command. If this
             *   field is set to 0, the device will expect that no data will be
             *   transferred IN or OUT during this command, regardless of the value
             *   of the Direction bit defined in dCBWFlags.
             */
        USETDW(sc->cbw.dCBWDataTransferLength, sc->sc_transfer.data_len);

        /*
             * dCBWFlags:
             *   The bits of the Flags field are defined as follows:
             *     Bits 0-6  reserved
             *     Bit  7    Direction - this bit shall be ignored if the
             *                           dCBWDataTransferLength field is zero.
             *               0 = data Out from host to device
             *               1 = data In from device to host
             */
        sc->cbw.bCBWFlags = ((sc->sc_transfer.dir == DIR_IN) ?
            CBWFLAGS_IN : CBWFLAGS_OUT);
        sc->cbw.bCBWLUN = sc->sc_transfer.lun;

        if (sc->sc_transfer.cmd_len > sizeof(sc->cbw.CBWCDB)) {
            sc->sc_transfer.cmd_len = sizeof(sc->cbw.CBWCDB);
            DPRINTF_UMASS(sc, UDMASS_BBB, "Truncating long command!\n");
        }
        sc->cbw.bCDBLength = sc->sc_transfer.cmd_len;

        /* copy SCSI command data */
        ret = memcpy_s(sc->cbw.CBWCDB, CBWCDBLENGTH,
            sc->sc_transfer.cmd_data, sc->sc_transfer.cmd_len);
        if (ret != EOK) {
            DPRINTF_UMASS(sc, UDMASS_BBB, "memcpy_s fail, %d\n", ret);
            return;
        }

        /* clear remaining command area */
        (void)memset_s(sc->cbw.CBWCDB + sc->sc_transfer.cmd_len,
            sizeof(sc->cbw.CBWCDB) - sc->sc_transfer.cmd_len, 0,
            sizeof(sc->cbw.CBWCDB) - sc->sc_transfer.cmd_len);

        DIF(UDMASS_BBB,  umass_bbb_dump_cbw(sc, &sc->cbw));

        pc = usbd_xfer_get_frame(xfer, 0);
        usbd_copy_in(pc, 0, &sc->cbw, sizeof(sc->cbw));
        usbd_xfer_set_frame_len(xfer, 0, sizeof(sc->cbw));

        usbd_transfer_submit(xfer);

        return;

    default:            /* Error */
        umass_tr_error(xfer, error);
        return;
    }
}

static void
umass_t_bbb_data_callback(struct usb_xfer *xfer, usb_error_t error,
    uint8_t xfer_index)
{
    struct umass_softc *sc = (struct umass_softc *)usbd_xfer_softc(xfer);
    uint32_t max_bulk = usbd_xfer_max_len(xfer);
    int actlen, sumlen;

    usbd_xfer_status(xfer, &actlen, &sumlen, (int *)NULL, (int *)NULL);

    switch (USB_GET_STATE(xfer)) {
    case USB_ST_TRANSFERRED:
        sc->sc_transfer.data_rem -= actlen;
        sc->sc_transfer.data_ptr += actlen;
        sc->sc_transfer.actlen += actlen;

        if (actlen < sumlen) {
            /* short transfer */
            sc->sc_transfer.data_rem = 0;
        }
    case USB_ST_SETUP: /*lint !e616*/
        DPRINTF_UMASS(sc, UDMASS_BBB, "max_bulk=%u, data_rem=%u\n",
            max_bulk, sc->sc_transfer.data_rem);

        if (sc->sc_transfer.data_rem == 0) {
            umass_transfer_start(sc, UMASS_T_BBB_STATUS);
            return;
        }
        if (max_bulk > sc->sc_transfer.data_rem) {
            max_bulk = sc->sc_transfer.data_rem;
        }
        usbd_xfer_set_timeout(xfer, sc->sc_transfer.data_timeout);

        usbd_xfer_set_frame_data(xfer, 0, sc->sc_transfer.data_ptr,
            max_bulk);

        usbd_transfer_submit(xfer);
        return;

    default:            /* Error */
        if (error == USB_ERR_CANCELLED) {
            umass_tr_error(xfer, error);
        } else {
            umass_transfer_start(sc, xfer_index);
        }
        return;
    }
}

static void
umass_t_bbb_data_read_callback(struct usb_xfer *xfer, usb_error_t error)
{
    umass_t_bbb_data_callback(xfer, error, UMASS_T_BBB_DATA_RD_CS);
}

static void
umass_t_bbb_data_rd_cs_callback(struct usb_xfer *xfer, usb_error_t error)
{
    umass_t_bbb_data_clear_stall_callback(xfer, UMASS_T_BBB_STATUS,
        UMASS_T_BBB_DATA_READ, error);
}

static void
umass_t_bbb_data_write_callback(struct usb_xfer *xfer, usb_error_t error)
{
    umass_t_bbb_data_callback(xfer, error, UMASS_T_BBB_DATA_WR_CS);
}

static void
umass_t_bbb_data_wr_cs_callback(struct usb_xfer *xfer, usb_error_t error)
{
    umass_t_bbb_data_clear_stall_callback(xfer, UMASS_T_BBB_STATUS,
        UMASS_T_BBB_DATA_WRITE, error);
}

static void
umass_t_bbb_status_callback(struct usb_xfer *xfer, usb_error_t error)
{
    struct umass_softc *sc = (struct umass_softc *)usbd_xfer_softc(xfer);
    union ccb *umass_ccb = sc->sc_transfer.ccb;
    struct usb_page_cache *pc;
    uint32_t residue;
    int actlen;

    usbd_xfer_status(xfer, &actlen, (int *)NULL, (int *)NULL, (int *)NULL);

    switch (USB_GET_STATE(xfer)) {
    case USB_ST_TRANSFERRED:

        /*
         * Do a full reset if there is something wrong with the CSW:
         */
        sc->sc_status_try = 1;

        /* Zero missing parts of the CSW: */

        if (actlen < (int)sizeof(sc->csw)) {
            (void)memset_s(&sc->csw, sizeof(sc->csw), 0, sizeof(sc->csw));
        }

        pc = usbd_xfer_get_frame(xfer, 0);

        usbd_copy_out(pc, 0, &sc->csw, actlen);

        DIF(UDMASS_BBB, umass_bbb_dump_csw(sc, &sc->csw));

        residue = UGETDW(sc->csw.dCSWDataResidue);

        if ((!residue) || (sc->sc_quirks & IGNORE_RESIDUE)) {
            residue = (sc->sc_transfer.data_len -
                sc->sc_transfer.actlen);
        }
        if (residue > sc->sc_transfer.data_len) {
            DPRINTF_UMASS(sc, UDMASS_BBB, "truncating residue from %d "
                "to %d bytes\n", residue, sc->sc_transfer.data_len);
            residue = sc->sc_transfer.data_len;
        }
        /* translate weird command-status signatures: */
        if (sc->sc_quirks & WRONG_CSWSIG) {

            uint32_t temp = UGETDW(sc->csw.dCSWSignature);

            if ((temp == CSWSIGNATURE_OLYMPUS_C1) ||
                (temp == CSWSIGNATURE_IMAGINATION_DBX1)) {
                USETDW(sc->csw.dCSWSignature, CSWSIGNATURE);
            }
        }
        /* check CSW and handle eventual error */
        if (UGETDW(sc->csw.dCSWSignature) != CSWSIGNATURE) {
            DPRINTF_UMASS(sc, UDMASS_BBB, "bad CSW signature 0x%08x != 0x%08x\n",
                UGETDW(sc->csw.dCSWSignature), CSWSIGNATURE);
            /*
             * Invalid CSW: Wrong signature or wrong tag might
             * indicate that we lost synchronization. Reset the
             * device.
             */
            goto tr_error;
        } else if (UGETDW(sc->csw.dCSWTag) != UGETDW(sc->cbw.dCBWTag)) {
            DPRINTF_UMASS(sc, UDMASS_BBB, "Invalid CSW: tag 0x%08x should be "
                "0x%08x\n", UGETDW(sc->csw.dCSWTag),
                UGETDW(sc->cbw.dCBWTag));
            goto tr_error;
        } else if (sc->csw.bCSWStatus > CSWSTATUS_PHASE) {
            DPRINTF_UMASS(sc, UDMASS_BBB, "Invalid CSW: status %d > %d\n",
                sc->csw.bCSWStatus, CSWSTATUS_PHASE);
            goto tr_error;
        } else if (sc->csw.bCSWStatus == CSWSTATUS_PHASE) {
            DPRINTF_UMASS(sc, UDMASS_BBB, "Phase error, residue = "
                "%d\n", residue);
            goto tr_error;
        } else if (sc->sc_transfer.actlen > sc->sc_transfer.data_len) {
            DPRINTF_UMASS(sc, UDMASS_BBB, "Buffer overrun %d > %d\n",
                sc->sc_transfer.actlen, sc->sc_transfer.data_len);
            goto tr_error;
        } else if (sc->csw.bCSWStatus == CSWSTATUS_FAILED) {
            DPRINTF_UMASS(sc, UDMASS_BBB, "Command failed, residue = "
                "%d\n", residue);

            sc->sc_transfer.ccb = (union ccb *)NULL;

            sc->sc_last_xfer_index = UMASS_T_BBB_COMMAND;

            (sc->sc_transfer.callback)
                (sc, umass_ccb, residue, STATUS_CMD_FAILED);
        } else {
            sc->sc_transfer.ccb = (union ccb *)NULL;

            sc->sc_last_xfer_index = UMASS_T_BBB_COMMAND;

            (sc->sc_transfer.callback)
                (sc, umass_ccb, residue, STATUS_CMD_OK);
        }
        return;

    case USB_ST_SETUP:
        usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
        usbd_transfer_submit(xfer);
        return;

    default:
tr_error:
        DPRINTF_UMASS(sc, UDMASS_BBB, "Failed to read CSW: %s, try %d\n",
            usbd_errstr(error), sc->sc_status_try);

        if ((error == USB_ERR_CANCELLED) ||
            (sc->sc_status_try)) {
            umass_tr_error(xfer, error);
        } else {
            sc->sc_status_try = 1;
            umass_transfer_start(sc, UMASS_T_BBB_DATA_RD_CS);
        }
        return;
    }
}

static int
umass_command_start(struct umass_softc *sc, uint8_t dir,
    void *data_ptr, uint32_t data_len,
    uint32_t data_timeout, umass_callback_t *callback,
    union ccb *umass_ccb)
{
    if (sc->sc_detach_status)
    {
        PRINT_WARN("[%s][%d] usb is detaching\n",__FUNCTION__,__LINE__);
        return -1;
    }

    /*
     * NOTE: assumes that "sc->sc_transfer.cmd_data" and
     * "sc->sc_transfer.cmd_len" has been properly
     * initialized.
     */

    sc->sc_transfer.dir = data_len ? dir : DIR_NONE;
    sc->sc_transfer.data_ptr = (uint8_t *)data_ptr;
    sc->sc_transfer.data_len = data_len;
    sc->sc_transfer.data_rem = data_len;
    sc->sc_transfer.data_timeout = (data_timeout + UMASS_TIMEOUT);

    sc->sc_transfer.actlen = 0;
    sc->sc_transfer.callback = callback;
    sc->sc_transfer.ccb = umass_ccb;

    if (sc->sc_xfer[sc->sc_last_xfer_index]) {
        usbd_transfer_start(sc->sc_xfer[sc->sc_last_xfer_index]);
    } else {
        umass_cancel_ccb(sc);
    }

    (void)LOS_EventRead(&sc->sc_event, 0xFF,
         LOS_WAITMODE_OR|LOS_WAITMODE_CLR, 1000);

    return 0;
}

static uint8_t
umass_bbb_get_max_lun(struct umass_softc *sc)
{
    struct usb_device_request req;
    usb_error_t err;
    uint8_t buf = 0;

    /* The Get Max Lun command is a class-specific request. */
    req.bmRequestType = UT_READ_CLASS_INTERFACE;
    req.bRequest = UR_BBB_GET_MAX_LUN;
    USETW(req.wValue, 0); /*lint !e572*/
    req.wIndex[0] = sc->sc_iface_no;
    req.wIndex[1] = 0;
    USETW(req.wLength, 1); /*lint !e572*/

    err = usbd_do_request(sc->sc_udev, (struct mtx *)NULL, &req, &buf);
    if (err) {
        buf = 0;

        /* Device doesn't support Get Max Lun request. */
        PRINTK("%s: Get Max Lun not supported (%s)\n",
            sc->sc_name, usbd_errstr(err));
    }
    return (buf);
}

/*
 * Command/Bulk/Interrupt (CBI) specific functions
 */

static void
umass_cbi_start_status(struct umass_softc *sc)
{
    if (sc->sc_xfer[UMASS_T_CBI_STATUS]) {
        umass_transfer_start(sc, UMASS_T_CBI_STATUS);
    } else {
        union ccb *umass_ccb = sc->sc_transfer.ccb;

        sc->sc_transfer.ccb = (union ccb *)NULL;

        sc->sc_last_xfer_index = UMASS_T_CBI_COMMAND;

        (sc->sc_transfer.callback)
            (sc, umass_ccb, (sc->sc_transfer.data_len -
            sc->sc_transfer.actlen), STATUS_CMD_UNKNOWN);
    }
}

static void
umass_t_cbi_reset1_callback(struct usb_xfer *xfer, usb_error_t error)
{
    struct umass_softc *sc = (struct umass_softc *)usbd_xfer_softc(xfer);
    struct usb_device_request req;
    struct usb_page_cache *pc;
    uint8_t buf[UMASS_CBI_DIAGNOSTIC_CMDLEN];

    uint8_t i;

    switch (USB_GET_STATE(xfer)) {
    case USB_ST_TRANSFERRED:
        umass_transfer_start(sc, UMASS_T_CBI_RESET2);
        break;

    case USB_ST_SETUP:
        /*
         * Command Block Reset Protocol
         *
         * First send a reset request to the device. Then clear
         * any possibly stalled bulk endpoints.
         *
         * This is done in 3 steps, using 3 transfers:
         * UMASS_T_CBI_RESET1
         * UMASS_T_CBI_RESET2
         * UMASS_T_CBI_RESET3
         * UMASS_T_CBI_RESET4 (only if there is an interrupt endpoint)
         */

        DPRINTF_UMASS(sc, UDMASS_CBI, "CBI reset!\n");

        req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
        req.bRequest = UR_CBI_ADSC;
        USETW(req.wValue, 0); /*lint !e572*/
        req.wIndex[0] = sc->sc_iface_no;
        req.wIndex[1] = 0;
        USETW(req.wLength, UMASS_CBI_DIAGNOSTIC_CMDLEN); /*lint !e572*/

        /*
         * The 0x1d code is the SEND DIAGNOSTIC command. To
         * distinguish between the two, the last 10 bytes of the CBL
         * is filled with 0xff (section 2.2 of the CBI
         * specification)
         */
        buf[0] = 0x1d;        /* Command Block Reset */
        buf[1] = 0x04;

        for (i = 2; i < UMASS_CBI_DIAGNOSTIC_CMDLEN; i++) {
            buf[i] = 0xff;
        }

        pc = usbd_xfer_get_frame(xfer, 0);
        usbd_copy_in(pc, 0, &req, sizeof(req));
        pc = usbd_xfer_get_frame(xfer, 1);
        usbd_copy_in(pc, 0, buf, sizeof(buf));

        usbd_xfer_set_frame_len(xfer, 0, sizeof(req));
        usbd_xfer_set_frame_len(xfer, 1, sizeof(buf));
        usbd_xfer_set_frames(xfer, 2);
        usbd_transfer_submit(xfer);
        break;

    default:            /* Error */
        if (error == USB_ERR_CANCELLED)
            umass_tr_error(xfer, error);
        else
            umass_transfer_start(sc, UMASS_T_CBI_RESET2);
        break;
    }
}

static void
umass_t_cbi_reset2_callback(struct usb_xfer *xfer, usb_error_t error)
{
    umass_t_cbi_data_clear_stall_callback(xfer, UMASS_T_CBI_RESET3,
        UMASS_T_CBI_DATA_READ, error);
}

static void
umass_t_cbi_reset3_callback(struct usb_xfer *xfer, usb_error_t error)
{
    struct umass_softc *sc = (struct umass_softc *)usbd_xfer_softc(xfer);

    umass_t_cbi_data_clear_stall_callback
        (xfer, (sc->sc_xfer[UMASS_T_CBI_RESET4] &&
        sc->sc_xfer[UMASS_T_CBI_STATUS]) ?
        UMASS_T_CBI_RESET4 : UMASS_T_CBI_COMMAND,
        UMASS_T_CBI_DATA_WRITE, error);
}

static void
umass_t_cbi_reset4_callback(struct usb_xfer *xfer, usb_error_t error)
{
    umass_t_cbi_data_clear_stall_callback(xfer, UMASS_T_CBI_COMMAND,
        UMASS_T_CBI_STATUS, error);
}

static void
umass_t_cbi_data_clear_stall_callback(struct usb_xfer *xfer,
    uint8_t next_xfer, uint8_t stall_xfer, usb_error_t error)
{
    struct umass_softc *sc = (struct umass_softc *)usbd_xfer_softc(xfer);

    switch (USB_GET_STATE(xfer)) {
    case USB_ST_TRANSFERRED:
tr_transferred:
        if (next_xfer == UMASS_T_CBI_STATUS) {
            umass_cbi_start_status(sc);
        } else {
            umass_transfer_start(sc, next_xfer);
        }
        break;

    case USB_ST_SETUP:
        if (usbd_clear_stall_callback(xfer, sc->sc_xfer[stall_xfer])) {
            goto tr_transferred;    /* should not happen */
        }
        break;

    default:            /* Error */
        umass_tr_error(xfer, error);
        break;
    }
}

static void
umass_t_cbi_command_callback(struct usb_xfer *xfer, usb_error_t error)
{
    struct umass_softc *sc = (struct umass_softc *)usbd_xfer_softc(xfer);
    union ccb *umass_ccb = sc->sc_transfer.ccb;
    struct usb_device_request req;
    struct usb_page_cache *pc;

    switch (USB_GET_STATE(xfer)) {
    case USB_ST_TRANSFERRED:

        if (sc->sc_transfer.dir == DIR_NONE) {
            umass_cbi_start_status(sc);
        } else {
            umass_transfer_start
                (sc, (sc->sc_transfer.dir == DIR_IN) ?
                UMASS_T_CBI_DATA_READ : UMASS_T_CBI_DATA_WRITE);
        }
        break;

    case USB_ST_SETUP:

        if (umass_ccb) {

            /*
                 * do a CBI transfer with cmd_len bytes from
                 * cmd_data, possibly a data phase of data_len
                 * bytes from/to the device and finally a status
                 * read phase.
                 */

            req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
            req.bRequest = UR_CBI_ADSC;
            USETW(req.wValue, 0); /*lint !e572*/
            req.wIndex[0] = sc->sc_iface_no;
            req.wIndex[1] = 0;
            req.wLength[0] = sc->sc_transfer.cmd_len;
            req.wLength[1] = 0;

            pc = usbd_xfer_get_frame(xfer, 0);
            usbd_copy_in(pc, 0, &req, sizeof(req));
            pc = usbd_xfer_get_frame(xfer, 1);
            usbd_copy_in(pc, 0, sc->sc_transfer.cmd_data,
                sc->sc_transfer.cmd_len);

            usbd_xfer_set_frame_len(xfer, 0, sizeof(req));
            usbd_xfer_set_frame_len(xfer, 1, sc->sc_transfer.cmd_len);
            usbd_xfer_set_frames(xfer,
                sc->sc_transfer.cmd_len ? 2 : 1);

            DIF(UDMASS_CBI,
                umass_cbi_dump_cmd(sc,
                sc->sc_transfer.cmd_data,
                sc->sc_transfer.cmd_len));

            usbd_transfer_submit(xfer);
        }
        break;

    default:            /* Error */
        /*
         * STALL on the control pipe can be result of the command error.
         * Attempt to clear this STALL same as for bulk pipe also
         * results in command completion interrupt, but ASC/ASCQ there
         * look like not always valid, so don't bother about it.
         */
        if ((error == USB_ERR_STALLED) ||
            (sc->sc_transfer.callback == &umass_cam_cb)) { /*lint !e546*/
            sc->sc_transfer.ccb = (union ccb *)NULL;
            (sc->sc_transfer.callback)
                (sc, umass_ccb, sc->sc_transfer.data_len,
                STATUS_CMD_UNKNOWN);
        } else {
            umass_tr_error(xfer, error);
            /* skip reset */
            sc->sc_last_xfer_index = UMASS_T_CBI_COMMAND;
        }
        break;
    }
}

static void
umass_t_cbi_data_read_callback(struct usb_xfer *xfer, usb_error_t error)
{
    struct umass_softc *sc = (struct umass_softc *)usbd_xfer_softc(xfer);
    uint32_t max_bulk = usbd_xfer_max_len(xfer);
    int actlen, sumlen;

    usbd_xfer_status(xfer, &actlen, &sumlen, (int *)NULL, (int *)NULL);

    switch (USB_GET_STATE(xfer)) {
    case USB_ST_TRANSFERRED:
        sc->sc_transfer.data_rem -= actlen;
        sc->sc_transfer.data_ptr += actlen;
        sc->sc_transfer.actlen += actlen;

        if (actlen < sumlen) {
            /* short transfer */
            sc->sc_transfer.data_rem = 0;
        }
    case USB_ST_SETUP: /*lint !e616*/
        DPRINTF_UMASS(sc, UDMASS_CBI, "max_bulk=%d, data_rem=%d\n",
            max_bulk, sc->sc_transfer.data_rem);

        if (sc->sc_transfer.data_rem == 0) {
            umass_cbi_start_status(sc);
            break;
        }
        if (max_bulk > sc->sc_transfer.data_rem) {
            max_bulk = sc->sc_transfer.data_rem;
        }
        usbd_xfer_set_timeout(xfer, sc->sc_transfer.data_timeout);

        usbd_xfer_set_frame_data(xfer, 0, sc->sc_transfer.data_ptr,
            max_bulk);

        usbd_transfer_submit(xfer);
        break;

    default:            /* Error */
        if ((error == USB_ERR_CANCELLED) ||
            (sc->sc_transfer.callback != &umass_cam_cb)) { /*lint !e546*/
            umass_tr_error(xfer, error);
        } else {
            umass_transfer_start(sc, UMASS_T_CBI_DATA_RD_CS);
        }
        break;
    }
}

static void
umass_t_cbi_data_rd_cs_callback(struct usb_xfer *xfer, usb_error_t error)
{
    umass_t_cbi_data_clear_stall_callback(xfer, UMASS_T_CBI_STATUS,
        UMASS_T_CBI_DATA_READ, error);
}

static void
umass_t_cbi_data_write_callback(struct usb_xfer *xfer, usb_error_t error)
{
    struct umass_softc *sc = (struct umass_softc *)usbd_xfer_softc(xfer);
    uint32_t max_bulk = usbd_xfer_max_len(xfer);
    int actlen, sumlen;

    usbd_xfer_status(xfer, &actlen, &sumlen, (int *)NULL, (int *)NULL);

    switch (USB_GET_STATE(xfer)) {
    case USB_ST_TRANSFERRED:
        sc->sc_transfer.data_rem -= actlen;
        sc->sc_transfer.data_ptr += actlen;
        sc->sc_transfer.actlen += actlen;

        if (actlen < sumlen) {
            /* short transfer */
            sc->sc_transfer.data_rem = 0;
        }
    case USB_ST_SETUP: /*lint !e616*/
        DPRINTF_UMASS(sc, UDMASS_CBI, "max_bulk=%d, data_rem=%d\n",
            max_bulk, sc->sc_transfer.data_rem);

        if (sc->sc_transfer.data_rem == 0) {
            umass_cbi_start_status(sc);
            break;
        }
        if (max_bulk > sc->sc_transfer.data_rem) {
            max_bulk = sc->sc_transfer.data_rem;
        }
        usbd_xfer_set_timeout(xfer, sc->sc_transfer.data_timeout);

        usbd_xfer_set_frame_data(xfer, 0, sc->sc_transfer.data_ptr,
            max_bulk);

        usbd_transfer_submit(xfer);
        break;

    default:            /* Error */
        if ((error == USB_ERR_CANCELLED) ||
        (sc->sc_transfer.callback != &umass_cam_cb)) { /*lint !e546*/
            umass_tr_error(xfer, error);
        } else {
            umass_transfer_start(sc, UMASS_T_CBI_DATA_WR_CS);
        }
        break;
    }
}

static void
umass_t_cbi_data_wr_cs_callback(struct usb_xfer *xfer, usb_error_t error)
{
    umass_t_cbi_data_clear_stall_callback(xfer, UMASS_T_CBI_STATUS,
        UMASS_T_CBI_DATA_WRITE, error);
}

static void
umass_t_cbi_status_callback(struct usb_xfer *xfer, usb_error_t error)
{
    struct umass_softc *sc = (struct umass_softc *)usbd_xfer_softc(xfer);
    union ccb *umass_ccb = sc->sc_transfer.ccb;
    struct usb_page_cache *pc;
    uint32_t residue;
    uint8_t status;
    int actlen;

    usbd_xfer_status(xfer, &actlen, (int *)NULL, (int *)NULL, (int *)NULL);

    switch (USB_GET_STATE(xfer)) {
    case USB_ST_TRANSFERRED:

        if (actlen < (int)sizeof(sc->sbl)) {
            goto tr_setup;
        }
        pc = usbd_xfer_get_frame(xfer, 0);
        usbd_copy_out(pc, 0, &sc->sbl, sizeof(sc->sbl));

        residue = (sc->sc_transfer.data_len -
            sc->sc_transfer.actlen);

        /* dissect the information in the buffer */

        if (sc->sc_proto & UMASS_PROTO_UFI) {

            /*
             * Section 3.4.3.1.3 specifies that the UFI command
             * protocol returns an ASC and ASCQ in the interrupt
             * data block.
             */

            DPRINTF_UMASS(sc, UDMASS_CBI, "UFI CCI, ASC = 0x%02x, "
                "ASCQ = 0x%02x\n", sc->sbl.ufi.asc,
                sc->sbl.ufi.ascq);

            status = (((sc->sbl.ufi.asc == 0) &&
                (sc->sbl.ufi.ascq == 0)) ?
                STATUS_CMD_OK : STATUS_CMD_FAILED);

            sc->sc_transfer.ccb = (union ccb *)NULL;

            sc->sc_last_xfer_index = UMASS_T_CBI_COMMAND;

            (sc->sc_transfer.callback)
                (sc, umass_ccb, residue, status);

            break;

        } else {

            /* Command Interrupt Data Block */

            DPRINTF_UMASS(sc, UDMASS_CBI, "type=0x%02x, value=0x%02x\n",
                sc->sbl.common.type, sc->sbl.common.value);

            if (sc->sbl.common.type == IDB_TYPE_CCI) {

                status = (sc->sbl.common.value & IDB_VALUE_STATUS_MASK);

                status = ((status == IDB_VALUE_PASS) ? STATUS_CMD_OK :
                    (status == IDB_VALUE_FAIL) ? STATUS_CMD_FAILED :
                    (status == IDB_VALUE_PERSISTENT) ? STATUS_CMD_FAILED :
                    STATUS_WIRE_FAILED);

                sc->sc_transfer.ccb = (union ccb *)NULL;

                sc->sc_last_xfer_index = UMASS_T_CBI_COMMAND;

                (sc->sc_transfer.callback)
                    (sc, umass_ccb, residue, status);

                break;
            }
        }

        /* fallthrough */

    case USB_ST_SETUP:
tr_setup:
        usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
        usbd_transfer_submit(xfer);
        break;

    default:            /* Error */
        DPRINTF_UMASS(sc, UDMASS_CBI, "Failed to read CSW: %s\n",
            usbd_errstr(error));
        umass_tr_error(xfer, error);
        break;
    }
}

/* umass_cam_cb
 *    finalise a completed CAM command
 */

static void
umass_cam_cb(struct umass_softc *sc, union ccb *umass_ccb, uint32_t residue,
    uint8_t status)
{
    umass_ccb->csio.resid = residue;
    umass_ccb->csio.status = status;

    switch (status) {
    case STATUS_CMD_OK:
        (void)LOS_EventWrite(&sc->sc_event, 0x01);
        break;

    case STATUS_CMD_UNKNOWN:
    case STATUS_CMD_FAILED:
        /* fetch sense data */
        (void)LOS_EventWrite(&sc->sc_event, 0x02);
        break;

    default:
        (void)LOS_EventWrite(&sc->sc_event, 0x04);
        break;
    }
}

/*
 * SCSI specific functions
 */

static uint8_t
umass_scsi_transform(struct umass_softc *sc, uint8_t *cmd_ptr,
    uint8_t cmd_len)
{
    int ret = EOK;

    if ((cmd_len == 0) ||
        (cmd_len > sizeof(sc->sc_transfer.cmd_data))) {
        DPRINTF_UMASS(sc, UDMASS_SCSI, "Invalid command "
            "length: %d bytes\n", cmd_len);
        return (0);        /* failure */
    }
    sc->sc_transfer.cmd_len = cmd_len;

    switch (cmd_ptr[0]) {
    case TEST_UNIT_READY:
        if (sc->sc_quirks & NO_TEST_UNIT_READY) {
            DPRINTF_UMASS(sc, UDMASS_SCSI, "Converted TEST_UNIT_READY "
                "to START_UNIT\n");
            ret = memset_s(sc->sc_transfer.cmd_data, sizeof(sc->sc_transfer.cmd_data), 0, cmd_len);
            if (ret != EOK) {
                usb_err("memset_s failed!, ret:%d\n", ret);
                return 0;
            }
            sc->sc_transfer.cmd_data[0] = START_STOP_UNIT;
            sc->sc_transfer.cmd_data[4] = SSS_START;
            return (1);
        }
        break;

    case INQUIRY:
        /*
         * some drives wedge when asked for full inquiry
         * information.
         */
        if (sc->sc_quirks & FORCE_SHORT_INQUIRY) {
            ret = memcpy_s(sc->sc_transfer.cmd_data, UMASS_MAX_CMDLEN, cmd_ptr, cmd_len); /*lint !e506*/
            if (ret != EOK) {
                usb_err("memcpy_s failed!, ret:%d\n", ret);
                return 0;
            }
            sc->sc_transfer.cmd_data[4] = SHORT_INQUIRY_LENGTH;
            return (1);
        }
        break;
    }

    ret = memcpy_s(sc->sc_transfer.cmd_data, UMASS_MAX_CMDLEN, cmd_ptr, cmd_len); /*lint !e506*/
    if (ret != EOK) {
        usb_err("memcpy_s failed!, ret:%d\n", ret);
        return 0;
    }

    return (1);
}

static uint8_t
umass_rbc_transform(struct umass_softc *sc, uint8_t *cmd_ptr, uint8_t cmd_len)
{
    int ret;

    if ((cmd_len == 0) ||
        (cmd_len > sizeof(sc->sc_transfer.cmd_data))) {
        DPRINTF_UMASS(sc, UDMASS_SCSI, "Invalid command "
            "length: %d bytes\n", cmd_len);
        return (0);        /* failure */
    }
    switch (cmd_ptr[0]) {
        /* these commands are defined in RBC: */
    case READ_10:
    case READ_CAPACITY:
    case START_STOP_UNIT:
    case SYNCHRONIZE_CACHE:
    case WRITE_10:
    case 0x2f:            /* VERIFY_10 is absent from
                     * scsi_all.h??? */
    case INQUIRY:
    case MODE_SELECT_10:
    case MODE_SENSE_10:
    case TEST_UNIT_READY:
    case WRITE_BUFFER:
        /*
         * The following commands are not listed in my copy of the
         * RBC specs. CAM however seems to want those, and at least
         * the Sony DSC device appears to support those as well
         */
    case REQUEST_SENSE:
    case PREVENT_ALLOW:

        ret = memcpy_s(sc->sc_transfer.cmd_data, UMASS_MAX_CMDLEN, cmd_ptr, cmd_len); /*lint !e506*/
        if (ret != EOK) {
            usb_err("memcpy_s failed!, ret:%d\n", ret);
            return 0;
        }

        if ((sc->sc_quirks & RBC_PAD_TO_12) && (cmd_len < 12)) {
            ret = memset_s(sc->sc_transfer.cmd_data + cmd_len,
                UMASS_MAX_CMDLEN - cmd_len, 0, 12 - cmd_len); /*lint !e506*/
            if (ret != EOK){
                usb_err("memset_s failed!, ret:%d\n", ret);
                return 0;
            }
            cmd_len = 12;
        }
        sc->sc_transfer.cmd_len = cmd_len;
        return (1);        /* sucess */

        /* All other commands are not legal in RBC */
    default:
        DPRINTF_UMASS(sc, UDMASS_SCSI, "Unsupported RBC "
            "command 0x%02x\n", cmd_ptr[0]);
        return (0);        /* failure */
    }
}

static uint8_t
umass_ufi_transform(struct umass_softc *sc, uint8_t *cmd_ptr,
    uint8_t cmd_len)
{
    int ret = EOK;

    if ((cmd_len == 0) ||
        (cmd_len > sizeof(sc->sc_transfer.cmd_data))) {
        DPRINTF_UMASS(sc, UDMASS_SCSI, "Invalid command "
            "length: %d bytes\n", cmd_len);
        return (0);        /* failure */
    }
    /* An UFI command is always 12 bytes in length */
    sc->sc_transfer.cmd_len = UFI_COMMAND_LENGTH;

    /* Zero the command data */
    ret = memset_s(sc->sc_transfer.cmd_data, UMASS_MAX_CMDLEN, 0, UFI_COMMAND_LENGTH); /*lint !e506*/
    if (ret != EOK) {
        usb_err("memset_s failed!, ret:%d\n", ret);
        return 0;
    }

    switch (cmd_ptr[0]) {
        /*
         * Commands of which the format has been verified. They
         * should work. Copy the command into the (zeroed out)
         * destination buffer.
         */
    case TEST_UNIT_READY:
        if (sc->sc_quirks & NO_TEST_UNIT_READY) {
            /*
             * Some devices do not support this command. Start
             * Stop Unit should give the same results
             */
            DPRINTF_UMASS(sc, UDMASS_UFI, "Converted TEST_UNIT_READY "
                "to START_UNIT\n");

            sc->sc_transfer.cmd_data[0] = START_STOP_UNIT;
            sc->sc_transfer.cmd_data[4] = SSS_START;
            return (1);
        }
        break;

    case REZERO_UNIT:
    case REQUEST_SENSE:
    case FORMAT_UNIT:
    case INQUIRY:
    case START_STOP_UNIT:
    case SEND_DIAGNOSTIC:
    case PREVENT_ALLOW:
    case READ_CAPACITY:
    case READ_10:
    case WRITE_10:
    case POSITION_TO_ELEMENT:    /* SEEK_10 */
    case WRITE_AND_VERIFY:
    case VERIFIED:
    case MODE_SELECT_10:
    case MODE_SENSE_10:
    case READ_12:
    case WRITE_12:
    case READ_FORMAT_CAPACITIES:
        break;

        /*
         * SYNCHRONIZE_CACHE isn't supported by UFI, nor should it be
         * required for UFI devices, so it is appropriate to fake
         * success.
         */
    case SYNCHRONIZE_CACHE:
        return (2);

    default:
        DPRINTF_UMASS(sc, UDMASS_SCSI, "Unsupported UFI "
            "command 0x%02x\n", cmd_ptr[0]);
        return (0);        /* failure */
    }

    ret = memcpy_s(sc->sc_transfer.cmd_data, UMASS_MAX_CMDLEN, cmd_ptr, cmd_len); /*lint !e506*/
    if (ret != EOK) {
        usb_err("memcpy_s failed!, ret:%d\n", ret);
        return 0;
    }

    return (1);            /* success */
}

/*
 * 8070i (ATAPI) specific functions
 */
static uint8_t
umass_atapi_transform(struct umass_softc *sc, uint8_t *cmd_ptr,
    uint8_t cmd_len)
{
    int ret = EOK;

    if ((cmd_len == 0) ||
        (cmd_len > sizeof(sc->sc_transfer.cmd_data))) {
        DPRINTF_UMASS(sc, UDMASS_SCSI, "Invalid command "
            "length: %d bytes\n", cmd_len);
        return (0);        /* failure */
    }
    /* An ATAPI command is always 12 bytes in length. */
    sc->sc_transfer.cmd_len = ATAPI_COMMAND_LENGTH;

    /* Zero the command data */
    ret = memset_s(sc->sc_transfer.cmd_data, UMASS_MAX_CMDLEN, 0, ATAPI_COMMAND_LENGTH); /*lint !e506*/
    if (ret != EOK) {
        usb_err("memset_s failed!, ret:%d\n", ret);
        return 0;
    }

    switch (cmd_ptr[0]) {
        /*
         * Commands of which the format has been verified. They
         * should work. Copy the command into the destination
         * buffer.
         */
    case INQUIRY:
        /*
         * some drives wedge when asked for full inquiry
         * information.
         */
        if (sc->sc_quirks & FORCE_SHORT_INQUIRY) {
            ret = memcpy_s(sc->sc_transfer.cmd_data, UMASS_MAX_CMDLEN, cmd_ptr, cmd_len); /*lint !e506*/
            if (ret != EOK) {
                usb_err("memcpy_s failed!, ret:%d\n", ret);
                return 0;
            }
            sc->sc_transfer.cmd_data[4] = SHORT_INQUIRY_LENGTH;
            return (1);
        }
        break;

    case TEST_UNIT_READY:
        if (sc->sc_quirks & NO_TEST_UNIT_READY) {
            DPRINTF_UMASS(sc, UDMASS_SCSI, "Converted TEST_UNIT_READY "
                "to START_UNIT\n");
            sc->sc_transfer.cmd_data[0] = START_STOP_UNIT;
            sc->sc_transfer.cmd_data[4] = SSS_START;
            return (1);
        }
        break;

    case REZERO_UNIT:
    case REQUEST_SENSE:
    case START_STOP_UNIT:
    case SEND_DIAGNOSTIC:
    case PREVENT_ALLOW:
    case READ_CAPACITY:
    case READ_10:
    case WRITE_10:
    case POSITION_TO_ELEMENT:    /* SEEK_10 */
    case SYNCHRONIZE_CACHE:
    case MODE_SELECT_10:
    case MODE_SENSE_10:
    case READ_BUFFER:
    case 0x42:            /* READ_SUBCHANNEL */
    case 0x43:            /* READ_TOC */
    case 0x44:            /* READ_HEADER */
    case 0x47:            /* PLAY_MSF (Play Minute/Second/Frame) */
    case 0x48:            /* PLAY_TRACK */
    case 0x49:            /* PLAY_TRACK_REL */
    case 0x4b:            /* PAUSE */
    case 0x51:            /* READ_DISK_INFO */
    case 0x52:            /* READ_TRACK_INFO */
    case 0x54:            /* SEND_OPC */
    case 0x59:            /* READ_MASTER_CUE */
    case 0x5b:            /* CLOSE_TR_SESSION */
    case 0x5c:            /* READ_BUFFER_CAP */
    case 0x5d:            /* SEND_CUE_SHEET */
    case 0xa1:            /* BLANK */
    case 0xa5:            /* PLAY_12 */
    case 0xa6:            /* EXCHANGE_MEDIUM */
    case 0xad:            /* READ_DVD_STRUCTURE */
    case 0xbb:            /* SET_CD_SPEED */
    case 0xe5:            /* READ_TRACK_INFO_PHILIPS */
        break;

    case READ_12:
    case WRITE_12:
    default:
        DPRINTF_UMASS(sc, UDMASS_SCSI, "Unsupported ATAPI "
            "command 0x%02x - trying anyway\n",
            cmd_ptr[0]);
        break;
    }

    ret = memcpy_s(sc->sc_transfer.cmd_data, UMASS_MAX_CMDLEN, cmd_ptr, cmd_len); /*lint !e506*/
    if (ret != EOK) {
        usb_err("memcpy_s failed!, ret:%d\n", ret);
        return 0;
    }

    return (1);            /* success */
}

static uint8_t
umass_no_transform(struct umass_softc *sc, uint8_t *cmd,
    uint8_t cmdlen)
{
    return (0);            /* failure */
}

#ifdef LOSCFG_USB_DEBUG
static void
umass_bbb_dump_cbw(struct umass_softc *sc, umass_bbb_cbw_t *cbw)
{
    uint8_t *c = cbw->CBWCDB;

    uint32_t dlen = UGETDW(cbw->dCBWDataTransferLength);
    uint32_t tag = UGETDW(cbw->dCBWTag);

    uint8_t clen = cbw->bCDBLength;
    uint8_t flags = cbw->bCBWFlags;
    uint8_t lun = cbw->bCBWLUN;

    DPRINTF_UMASS(sc, UDMASS_BBB, "CBW %d: cmd = %db "
        "(0x%02x%02x%02x%02x%02x%02x%s), "
        "data = %db, lun = %d, dir = %s\n",
        tag, clen,
        c[0], c[1], c[2], c[3], c[4], c[5], (clen > 6 ? "..." : ""),
        dlen, lun, (flags == CBWFLAGS_IN ? "in" :
        (flags == CBWFLAGS_OUT ? "out" : "<invalid>")));
}

static void
umass_bbb_dump_csw(struct umass_softc *sc, umass_bbb_csw_t *csw)
{
    uint32_t sig = UGETDW(csw->dCSWSignature);
    uint32_t tag = UGETDW(csw->dCSWTag);
    uint32_t res = UGETDW(csw->dCSWDataResidue);
    uint8_t status = csw->bCSWStatus;

    DPRINTF_UMASS(sc, UDMASS_BBB, "CSW %d: sig = 0x%08x (%s), tag = 0x%08x, "
        "res = %d, status = 0x%02x (%s)\n",
        tag, sig, (sig == CSWSIGNATURE ? "valid" : "invalid"),
        tag, res,
        status, (status == CSWSTATUS_GOOD ? "good" :
        (status == CSWSTATUS_FAILED ? "failed" :
        (status == CSWSTATUS_PHASE ? "phase" : "<invalid>"))));
}

static void
umass_cbi_dump_cmd(struct umass_softc *sc, void *cmd, uint8_t cmdlen)
{
    uint8_t *c = cmd;
    uint8_t dir = sc->sc_transfer.dir;

    DPRINTF_UMASS(sc, UDMASS_BBB, "cmd = %db "
        "(0x%02x%02x%02x%02x%02x%02x%s), "
        "data = %db, dir = %s\n",
        cmdlen,
        c[0], c[1], c[2], c[3], c[4], c[5], (cmdlen > 6 ? "..." : ""),
        sc->sc_transfer.data_len,
        (dir == DIR_IN ? "in" :
        (dir == DIR_OUT ? "out" :
        (dir == DIR_NONE ? "no data phase" : "<invalid>"))));
}

#endif

#define    SCSI_INQ_LEN    0x24

static uint8_t scsi_test_unit_ready[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
static uint8_t scsi_inquiry[] = { 0x12, 0x00, 0x00, 0x00, SCSI_INQ_LEN, 0x00 };
static uint8_t scsi_request_sense[] =    { 0x03, 0x00, 0x00, 0x00, 0x12, 0x00,
                      0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
static uint8_t scsi_read_capacity[] =    { 0x25, 0x00, 0x00, 0x00, 0x00, 0x00,
                      0x00, 0x00, 0x00, 0x00 };

static uint8_t scsi_read_capacity_16[] =    { 0x9e, 0x10, 0x00, 0x00, 0x00, 0x00,
                      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00};

/* Description: Get a (possibly unaligned) 16-bit big endian value.*/
static inline uint16_t usbhost_getbe16(const uint8_t *val)
{
    return (uint16_t)val[0] << 8 | (uint16_t)val[1];
}

/* Description: Put a (possibly unaligned) 16-bit little endian value.*/
void usbhost_putle16(uint8_t *dest, uint16_t val)
{
    dest[0] = val & 0xff; /* Little endian means LS byte first in byte stream */
    dest[1] = val >> 8;
}

/* Description: Put a (possibly unaligned) 16-bit big endian value.*/
void usbhost_putbe16(uint8_t *dest, uint16_t val)
{
    dest[0] = val >> 8; /* Big endian means MS byte first in byte stream */
    dest[1] = val & 0xff;
}

/* Description: Get a (possibly unaligned) 32-bit big endian value.*/
uint32_t usbhost_getbe32(const uint8_t *val)
{
    /* Big endian means MS halfword first in byte stream */
    return (uint32_t)usbhost_getbe16(val) << 16 |
        (uint32_t)usbhost_getbe16(&val[2]);
}

/* Description: Get a (possibly unaligned) 64-bit big endian value.*/
uint64_t usbhost_getbe64(const uint8_t *val)
{
    /* Big endian means MS halfword first in byte stream */
    return (uint64_t)usbhost_getbe32(val) << 32 |
        (uint64_t)usbhost_getbe32(&val[4]);
}

/* Description: Put a (possibly unaligned) 32-bit little endian value.*/
void usbhost_putle32(uint8_t *dest, uint32_t val)
{
    /* Little endian means LS halfword first in byte stream */
    usbhost_putle16(dest, (uint16_t)(val & 0xffff));
    usbhost_putle16(dest+2, (uint16_t)(val >> 16));
}

/* Put a (possibly unaligned) 32-bit big endian value.*/
void usbhost_putbe32(uint8_t *dest, uint32_t val)
{
    /* Big endian means MS halfword first in byte stream */
    usbhost_putbe16(dest, (uint16_t)(val >> 16));
    usbhost_putbe16(dest+2, (uint16_t)(val & 0xffff));
}

/* Put a (possibly unaligned) 64-bit big endian value.*/
void usbhost_putbe64(uint8_t *dest, uint64_t val)
{
    /* Big endian means MS halfword first in byte stream */
    usbhost_putbe32(dest, (uint32_t)(val >> 32));
    usbhost_putbe32(dest+4, (uint32_t)(val & 0xffffffff));
}

void
usbhost_readcdb16(uint64_t startsector, uint16_t blocksize,
    unsigned int nsectors, struct scsicmd_read16_s *cdb)
{
    struct scsicmd_read16_s *rd16 = (struct scsicmd_read16_s *)cdb;

    /* Format the CDB */
    rd16->opcode = SCSI_CMD_READ16;
    usbhost_putbe64(rd16->lba, startsector);
    usbhost_putbe32(rd16->xfrlen, nsectors);
}

void
usbhost_readcdb10(size_t startsector, uint16_t blocksize,
    unsigned int nsectors, struct scsicmd_read10_s *cdb)
{
    struct scsicmd_read10_s *rd10 = (struct scsicmd_read10_s *)cdb;

    /* Format the CDB */
    rd10->opcode = SCSI_CMD_READ10;
    usbhost_putbe32(rd10->lba, startsector);
    usbhost_putbe16(rd10->xfrlen, nsectors);
}

void
usbhost_writecbw16(uint64_t startsector, uint16_t blocksize,
    unsigned int nsectors, struct scsicmd_write16_s *cdb)
{
    struct scsicmd_write16_s *wr16 = (struct scsicmd_write16_s *)cdb;

    wr16->opcode = SCSI_CMD_WRITE16;
    usbhost_putbe64(wr16->lba, startsector);
    usbhost_putbe32(wr16->xfrlen, nsectors);
}

void
usbhost_writecbw10(size_t startsector, uint16_t blocksize,
    unsigned int nsectors, struct scsicmd_write10_s *cdb)
{
    struct scsicmd_write10_s *wr10 = (struct scsicmd_write10_s *)cdb;

    wr10->opcode = SCSI_CMD_WRITE10;
    usbhost_putbe32(wr10->lba, startsector);
    usbhost_putbe16(wr10->xfrlen, nsectors);
}

int umass_TestUnitReady(struct umass_softc *sc)
{
    uint32_t status;
    int32_t res;

    if(sc == NULL || sc->data_ccb == NULL) {
        return -1;
    }

    (void)umass_scsi_transform(sc, scsi_test_unit_ready, SCSICMD_TESTUNITREADY8_SIZEOF);
    res = umass_command_start(sc, DIR_NONE, NULL, 0, 1000, umass_cam_cb, sc->data_ccb);
    if (STATUS_CMD_OK != res) {
        return -1;
    }

    status = sc->data_ccb->csio.status;
    if (status != STATUS_CMD_OK) {
        return -1;
    }

    return 0;
}

int umass_Read_Capacity_16(struct umass_softc *sc)
{
    struct scsiresp_readcapacity16_s resp;
    uint32_t res;
    int ret;

    (void)umass_scsi_transform(sc, scsi_read_capacity_16, SCSICMD_READCAPACITY16_SIZEOF);
    res = umass_command_start(sc, DIR_IN, sc->data_ccb->csio.data_ptr, SCSIRESP_READCAPACITY16_SIZEOF,
        1000, umass_cam_cb, sc->data_ccb);
    if (STATUS_CMD_OK != res) {
        return -1;
    }

    ret = memcpy_s((void *)&resp, sizeof(resp), sc->data_ccb->csio.data_ptr, SCSIRESP_READCAPACITY16_SIZEOF);
    if (ret != EOK) {
        usb_err("memcpy_s failed, %d\n", ret);
        return -1;
    }

    sc->info.sectornum= usbhost_getbe64(resp.lba) + 1;
    sc->info.sectorsize= usbhost_getbe32(resp.blklen);

    return 0;
}

int umass_Read_Capacity(struct umass_softc *sc)
{
    struct scsiresp_readcapacity10_s resp;
    int32_t ret;

    if (sc == NULL || sc->data_ccb == NULL) {
        return -1;
    }

    (void)umass_scsi_transform(sc, scsi_read_capacity, SCSICMD_READCAPACITY10_SIZEOF);
    ret = umass_command_start(sc, DIR_IN, sc->data_ccb->csio.data_ptr, SCSIRESP_READCAPACITY10_SIZEOF,
        1000, umass_cam_cb, sc->data_ccb);
    if (STATUS_CMD_OK != ret) {
        return -1;
    }

    ret = memcpy_s((void *)&resp, sizeof(resp), sc->data_ccb->csio.data_ptr, SCSIRESP_READCAPACITY10_SIZEOF);
    if (ret != EOK) {
        usb_err("memcpy_s failed, %d\n", ret);
        return -1;
    }

    /* The disk Capacity is bigger than 2T */
    if (usbhost_getbe32(resp.lba) == 0xffffffff) {
        ret = umass_Read_Capacity_16(sc);
        if (ret != 0) {
            usb_err("Read Capacity failed, %d\n", ret);
            return -1;
        }
        sc->sc_super_disk = TRUE;

        return 0;
    }

    sc->info.sectornum= usbhost_getbe32(resp.lba) + 1;
    sc->info.sectorsize= usbhost_getbe32(resp.blklen);
    sc->sc_super_disk = FALSE;

    return 0;
}

int umass_Read10(struct umass_softc *sc, size_t startsector, uint16_t blocksize, unsigned int
nsectors, unsigned char *buf)
{
    struct scsicmd_read10_s cdb;
    uint8_t *data_buf = buf;
    uint32_t status;
    int32_t ret;
    uint32_t flag = 0;

    if (sc == NULL || sc->data_ccb == NULL) {
        return -1;
    }

    if ((sc->info.sectornum < (startsector + nsectors)) || (sc->info.sectorsize < blocksize))
        return -1;

    if (((uintptr_t)data_buf & (USB_CACHE_ALIGN_SIZE - 1)) != 0)
    {
        data_buf = (uint8_t *)memalign(USB_CACHE_ALIGN_SIZE, SKB_DATA_ALIGN(nsectors * blocksize));
        if (data_buf == NULL)
        {
            PRINT_ERR("Malloc failed!->%s %d\n", __FUNCTION__, __LINE__);
            return -1;
        }
        flag = 1;
    }

    (void)memset_s(&cdb, sizeof(struct scsicmd_read10_s), 0, sizeof(struct scsicmd_read10_s));
    usbhost_readcdb10(startsector, blocksize, nsectors, &cdb);
    (void)umass_scsi_transform(sc, (uint8_t *)&cdb, SCSICMD_READ10_SIZEOF);
    ret = umass_command_start(sc, DIR_IN, (void *)data_buf, blocksize * nsectors, 0,
        umass_cam_cb, sc->data_ccb);
    if (STATUS_CMD_OK != ret)
    {
        if (flag == 1)
            free(data_buf);
        return -1;
    }

    if (flag == 1) {
        ret = memcpy_s(buf, nsectors * blocksize, data_buf, blocksize * nsectors - sc->data_ccb->csio.resid);
        free(data_buf);
        if (ret != EOK) {
            return -1;
        }
    }

    status = sc->data_ccb->csio.status;
    if (status != STATUS_CMD_OK) {
        return -1;
    }

    return 0;
}

int umass_Read16(struct umass_softc *sc, uint64_t startsector, uint16_t blocksize, unsigned int
nsectors, unsigned char *buf)
{
    struct scsicmd_read16_s cdb;
    uint8_t *data_buf = buf;
    uint32_t status;
    uint32_t res;
    uint32_t flag = 0;
    int ret;

    if (sc == NULL || sc->data_ccb == NULL) {
        return -1;
    }

    if ((sc->info.sectornum < (startsector + nsectors)) || (sc->info.sectorsize < blocksize))
        return -1;

    if (((uintptr_t)data_buf & (USB_CACHE_ALIGN_SIZE - 1)) != 0)
    {
        data_buf = (uint8_t *)memalign(USB_CACHE_ALIGN_SIZE, SKB_DATA_ALIGN(nsectors * blocksize));
        if (data_buf == NULL)
        {
            PRINT_ERR("Malloc failed!->%s %d\n", __FUNCTION__, __LINE__);
            return -1;
        }
        flag = 1;
    }

    (void)memset_s(&cdb, sizeof(struct scsicmd_read16_s), 0, sizeof(struct scsicmd_read16_s));
    usbhost_readcdb16(startsector, blocksize, nsectors, &cdb);
    (void)umass_scsi_transform(sc, (uint8_t *)&cdb, SCSICMD_READ16_SIZEOF);
    res = umass_command_start(sc, DIR_IN, (void *)data_buf, blocksize * nsectors, 0,
        umass_cam_cb, sc->data_ccb);
    if (STATUS_CMD_OK != res)
    {
        if (flag == 1)
            free(data_buf);
        return -1;
    }

    if (flag == 1) {
        ret = memcpy_s(buf, nsectors * blocksize, data_buf, blocksize * nsectors - sc->data_ccb->csio.resid);
        free(data_buf);
        if (ret != EOK) {
            return -1;
        }
    }

    status = sc->data_ccb->csio.status;
    if (status != STATUS_CMD_OK) {
        return -1;
    }

    return 0;
}

int umass_Write10(struct umass_softc *sc, size_t startsector, uint16_t blocksize, unsigned int
nsectors, const unsigned char *buf)
{
    struct scsicmd_write10_s cdb;
    uint8_t *data_buf = (uint8_t *)buf;
    uint32_t status;
    int32_t ret;
    uint32_t flag = 0;

    if(sc == NULL || sc->data_ccb == NULL) {
        return -1;
    }

    if ((sc->info.sectornum < (startsector + nsectors)) || (sc->info.sectorsize < blocksize))
        return -1;

    if (((uintptr_t)data_buf & (USB_CACHE_ALIGN_SIZE - 1)) != 0)
    {
        data_buf = (uint8_t *)memalign(USB_CACHE_ALIGN_SIZE, SKB_DATA_ALIGN(nsectors * blocksize));
        if (data_buf == NULL)
        {
            PRINT_ERR("Malloc failed!->%s %d\n", __FUNCTION__, __LINE__);
            return -1;
        }
        (void)memcpy_s(data_buf, blocksize * nsectors, buf, blocksize * nsectors);
        flag = 1;
    }

    (void)memset_s(&cdb, sizeof(struct scsicmd_write10_s), 0, sizeof(struct scsicmd_write10_s));
    usbhost_writecbw10(startsector, blocksize, nsectors, &cdb);
    (void)umass_scsi_transform(sc, (uint8_t *)&cdb, SCSICMD_WRITE10_SIZEOF);
    ret = umass_command_start(sc, DIR_OUT, (void *)data_buf, blocksize * nsectors, 1000,
        umass_cam_cb, sc->data_ccb);

    if (flag == 1)
    {
        free(data_buf);
    }

    status = sc->data_ccb->csio.status;
    if (STATUS_CMD_OK != ret || status != STATUS_CMD_OK) {
        return -1;
    }

    return 0;
}

int umass_Write16(struct umass_softc *sc, uint64_t startsector, uint16_t blocksize, unsigned int
nsectors, const unsigned char *buf)
{
    struct scsicmd_write16_s cdb;
    uint8_t *data_buf = (uint8_t *)buf;
    uint32_t status;
    int32_t  res;
    uint32_t flag = 0;

    if(sc == NULL || sc->data_ccb == NULL) {
        return -1;
    }

    if ((sc->info.sectornum < (startsector + nsectors)) || (sc->info.sectorsize < blocksize)) {
        return -1;
    }

    if (((uintptr_t)data_buf & (USB_CACHE_ALIGN_SIZE - 1)) != 0)
    {
        data_buf = (uint8_t *)memalign(USB_CACHE_ALIGN_SIZE, SKB_DATA_ALIGN(nsectors * blocksize));
        if (data_buf == NULL)
        {
            PRINT_ERR("Malloc failed!->%s %d\n", __FUNCTION__, __LINE__);
            return -1;
        }
        (void)memcpy_s(data_buf, blocksize * nsectors, buf, blocksize * nsectors);
        flag = 1;
    }

    (void)memset_s(&cdb, sizeof(struct scsicmd_write16_s), 0, sizeof(struct scsicmd_write16_s));
    usbhost_writecbw16(startsector, blocksize, nsectors, &cdb);
    (void)umass_scsi_transform(sc, (uint8_t *)&cdb, SCSICMD_WRITE16_SIZEOF);
    res = umass_command_start(sc, DIR_OUT, (void *)data_buf, blocksize * nsectors, 1000,
        umass_cam_cb, sc->data_ccb);

    if (flag == 1)
    {
        free(data_buf);
    }

    status = sc->data_ccb->csio.status;
    if (STATUS_CMD_OK != res || status != STATUS_CMD_OK) {
        return -1;
    }

    return 0;
}

int umass_Inquiry(struct umass_softc *sc)
{
    uint32_t status;
    int32_t ret;

    if (sc == NULL || sc->data_ccb == NULL) {
        goto error;
    }

    (void)umass_scsi_transform(sc, scsi_inquiry, SCSICMD_INQUIRY_SIZEOF);
    ret = umass_command_start(sc, DIR_IN, sc->data_ccb->csio.data_ptr, SCSIRESP_INQUIRY_SIZEOF,
        1000, umass_cam_cb, sc->data_ccb);
    if (STATUS_CMD_OK != ret) {
        goto error;
    }

    status = sc->data_ccb->csio.status;
    if (status != STATUS_CMD_OK) {
        PRINT_WARN("Failed to get the inquiry_status [status=%d].\n", status);
        goto error;
    }

    ret = umass_scsi_inquiry_data(sc, sc->data_ccb->csio.data_ptr, SCSIRESP_INQUIRY_SIZEOF);
    if (ret == -1){
        PRINT_WARN("Failed to get the scsi_inquiry data .\n");
        goto error;
    }else if (ret == 1) {
        /* find Direct-access LUN */
        return (0);
    }

error:
    return (-1);
}

int umass_Request_Sense(struct umass_softc *sc)
{
    uint32_t status;
    int32_t ret;
    if (sc == NULL || sc->data_ccb == NULL) {
        return -1;
    }

    (void)umass_scsi_transform(sc, scsi_request_sense, SCSICMD_REQUESTSENSE_SIZEOF);
    ret = umass_command_start(sc, DIR_IN, sc->data_ccb->csio.data_ptr, SCSIRESP_FIXEDSENSEDATA_SIZEOF,
        1000, umass_cam_cb, sc->data_ccb);
    if (STATUS_CMD_OK != ret) {
        return -1;
    }

    status = sc->data_ccb->csio.status;
    if (status != STATUS_CMD_OK) {
        return -1;
    }

    return 0;
}

void* umass_bind(void)
{
    return (void*)p_umsf;
}

void umass_status(void)
{
    struct umass_softc *sc = p_umsf;
    UINT8 cmd, lun, max, speed, phase, state;
    UINT16 vid, pid;
    UINT32 tag, residuce;

    if (sc == NULL) {
        return;
    }

    cmd = sc->sc_transfer.cmd_data[0];
    lun = sc->sc_transfer.lun;
    max = sc->sc_maxlun + 1;
    speed = sc->sc_udev->speed;
    phase = sc->sc_last_xfer_index;
    state = USB_GET_STATE(sc->sc_xfer[phase]);
    vid = UGETW(sc->sc_udev->ddesc.idVendor);
    pid = UGETW(sc->sc_udev->ddesc.idProduct);
    tag = UGETDW(sc->cbw.dCBWTag);
    residuce = UGETDW(sc->csw.dCSWDataResidue);

    dprintf("VID:%04X/PID:%04X/SPD:%02d",vid,pid,speed);
    if(sc->sc_transfer.ccb){
        dprintf("/ST:%02d ",state);
        if(state == USB_ST_SETUP){dprintf("[SP]");}
        else if(state == USB_ST_TRANSFERRED){dprintf("[TD]");}
        dprintf("/cPHASE:%02d ",phase);
    }else{
        dprintf("/nPHASE:%02d ",phase);
    }
    if(phase == UMASS_T_BBB_COMMAND){dprintf("[CBW]");}
    else if(phase == UMASS_T_BBB_DATA_READ){dprintf("[DATA]");}
    else if(phase == UMASS_T_BBB_DATA_WRITE){dprintf("[DATA]");}
    else if(phase == UMASS_T_BBB_STATUS){dprintf("[CSW]");}
    else if(phase == UMASS_T_BBB_DATA_RD_CS){dprintf("[STAL]");}
    else if(phase == UMASS_T_BBB_DATA_WR_CS){dprintf("[STAL]");}
    dprintf("\n");

    dprintf("CL:%d/ML:%d/TG:%08X/RDU:%d/CMD:%X ",lun,max,tag,residuce,cmd);
    if(cmd == SCSI_CMD_READ10){dprintf("[RD]\n");}
    else if(cmd == SCSI_CMD_WRITE10){dprintf("[WR]\n");}
    else if(cmd == SCSI_CMD_INQUIRY){dprintf("[INQ]\n");}
    else if(cmd == SCSI_CMD_TESTUNITREADY){dprintf("[TUR]\n");}
    else if(cmd == SCSI_CMD_REQUESTSENSE){dprintf("[RS]\n");}
    else if(cmd == SCSI_CMD_READCAPACITY10){dprintf("[RC]\n");}
    else{dprintf("\n");}
}

static int  umass_open(FAR struct inode *filep)
{
    return 0;
}

static int  umass_close(FAR struct inode *filep)
{
    return 0;
}

static ssize_t umass_read(FAR struct inode *umass_inode, FAR unsigned char *buffer,
            uint64_t start_sector, unsigned int nsectors)
{
    int status = -1;
    struct umass_softc *sc = (struct umass_softc *)umass_inode->i_private;

    mtx_lock(&sc->sc_umass_mtx);
    if (sc->sc_super_disk == TRUE)
    {
        status = umass_Read16(sc, start_sector, sc->info.sectorsize, nsectors, buffer);
    }
    else
    {
        status = umass_Read10(sc, start_sector, sc->info.sectorsize, nsectors, buffer);
    }
    mtx_unlock(&sc->sc_umass_mtx);

    if(status)
        return -1;
    else
        return nsectors;
}

static ssize_t umass_write(FAR struct inode *umass_inode, FAR const unsigned char *buffer,
            uint64_t start_sector, unsigned int nsectors)
{
    int status = -1;
    struct umass_softc *sc = (struct umass_softc *)umass_inode->i_private;

    mtx_lock(&sc->sc_umass_mtx);
    if (sc->sc_super_disk == TRUE)
    {
        status = umass_Write16(sc, start_sector, sc->info.sectorsize, nsectors, buffer);
    }
    else
    {
        status = umass_Write10(sc, start_sector, sc->info.sectorsize, nsectors, buffer);
    }
    mtx_unlock(&sc->sc_umass_mtx);

    if(status)
        return -1;
    else
        return nsectors;
}

static int umass_geometry(FAR struct inode *umass_inode, FAR struct geometry *ugeometry)
{
    struct umass_softc *sc = NULL;

    if (ugeometry == NULL || umass_inode == NULL)
        return -1;

    sc = (struct umass_softc *)umass_inode->i_private;

    if (sc == NULL)
        return  -1;

    mtx_lock(&sc->sc_umass_mtx);
    ugeometry->geo_available    = true;
    ugeometry->geo_mediachanged = false;
    ugeometry->geo_writeenabled = true;
    ugeometry->geo_nsectors     = sc->info.sectornum;
    ugeometry->geo_sectorsize   = sc->info.sectorsize;
    mtx_unlock(&sc->sc_umass_mtx);

    return 0;
}

static int umass_ioctl(FAR struct inode *umass_inode, int cmd, unsigned long arg)
{
    return 0;
}

struct block_operations g_dev_umass_ops =
{
    .open  = umass_open,
    .close = umass_close,
    .read  = umass_read,
    .write = umass_write,
    .geometry = umass_geometry,
    .ioctl = umass_ioctl,
    .unlink = NULL
}; /*lint !e64*/

static int umass_dev_is_ready(struct umass_softc *sc)
{
    int lun;
    int ret = 0;
    uint8_t valid_lun = 0;

    mtx_lock(&sc->sc_umass_mtx);
    for (lun = 0; lun <= sc->sc_maxlun; lun++) {
        sc->sc_transfer.lun = lun;
        if (umass_Inquiry(sc) < 0)
            continue;

        valid_lun = lun;
        ret = umass_TestUnitReady(sc);
        if(ret == 0) {
            sc->sc_transfer.lun = valid_lun;
            ret = umass_Read_Capacity(sc);
            if (ret < 0) {
                mtx_unlock(&sc->sc_umass_mtx);
                PRINT_ERR("umass read capacity fail!\n");
                return 0;
            }
            mtx_unlock(&sc->sc_umass_mtx);
            return 1;
        }

        ret = umass_Request_Sense(sc);
        if(ret < 0) {
            PRINT_ERR("Request sense fail!\n");
            mtx_unlock(&sc->sc_umass_mtx);
            return 0;
        }
    }
    mtx_unlock(&sc->sc_umass_mtx);
    return 0;
}

static int umass_attach_dev_sub(struct umass_softc *sc, unsigned int dev_unit)
{
    int ret = 0;
    int disk_id;
    char devname[MASS_NAME]= {0};
#if USB_HAVE_DEVICE_TOPOLOGY
    device_t dev = NULL;
    struct usb_device *udev = NULL;
    usbd_btNode *curNode = NULL;
    struct node_info parent_info, cur_info;
#endif

    umass_dev_lock(dev_unit);

#if USB_HAVE_DEVICE_TOPOLOGY
    dev = sc->sc_dev;
    udev = sc->sc_udev;
    dev_quantity |= 1ull << device_get_unit(dev);
#endif

#if USB_SUPPORT_SD_HOT_PLUG
    if (!umass_dev_is_attached(dev_unit))
#endif
    {
        devunit_to_devname(dev_unit, devname);
        disk_id = los_alloc_diskid_byname(devname);
        os_set_usb_status(disk_id);
        ret = los_disk_init(devname, &g_dev_umass_ops, (void *)sc, disk_id, NULL);
        if (ret) {
            PRINT_ERR("umass_attach_dev : los_disk_init fail!\n");
            goto error;
        }
    }
#if USB_SUPPORT_SD_HOT_PLUG
    umass_dev_attach_flag_set(dev_unit);
#endif

#if USB_HAVE_DEVICE_TOPOLOGY
    cur_info.nameunit = device_get_nameunit(dev);
    cur_info.port_no = udev->port_no;
    curNode = usbd_create_btNode(&cur_info);
    if (curNode == NULL) {
        goto error;
    }

    parent_info.nameunit = device_get_nameunit(device_get_parent(dev));
    parent_info.port_no = udev->port_no;

    (void)usbd_insert_btNode(curNode, hub_tree, &parent_info);
#endif

    umass_dev_unlock(dev_unit);
    return 0;

error:
    umass_dev_unlock(dev_unit);
    return -1;
}

static void umass_detach_dev_sub(struct umass_softc *sc, int dev_unit, int flag)
{
    int disk_id;
    char devname[MASS_NAME]= {0};
#if USB_HAVE_DEVICE_TOPOLOGY
    struct node_info cur_info, parent_info;
    device_t dev = NULL;
    struct usb_device *udev = NULL;
#endif

    umass_dev_lock(dev_unit);

#if USB_HAVE_DEVICE_TOPOLOGY
    dev = sc->sc_dev;
    udev = sc->sc_udev;
#if USB_SUPPORT_SD_HOT_PLUG
    if (umass_dev_is_attached(dev_unit))
#endif
    {
        parent_info.nameunit = device_get_nameunit(device_get_parent(dev));
        parent_info.port_no = udev->port_no;

        cur_info.nameunit = dev->dev_nameunit;
        cur_info.port_no = udev->port_no;
        (void)usbd_remove_btNode(hub_tree, &parent_info, &cur_info);
    }
#endif

#if USB_SUPPORT_SD_HOT_PLUG
    if (umass_dev_is_attached(dev_unit))
#endif
    {
        devunit_to_devname(dev_unit, devname);
        disk_id = los_get_diskid_byname(devname);
        (void)los_disk_deinit(disk_id);
        (void)os_clear_usb_status(disk_id);
    }

    if (flag == 0) { //0: This interface is called from umass_detach, or is called elsewhere.
#if USB_SUPPORT_SD_HOT_PLUG
        umass_dev_delete(sc, dev_unit);
#endif
    }

#if USB_HAVE_DEVICE_TOPOLOGY
    dev_quantity &= ~(1ull << device_get_unit(dev));
#endif

    umass_dev_unlock(dev_unit);
}

#if USB_SUPPORT_SD_HOT_PLUG
void umass_dev_status_check(AARCHPTR arg)
{
    int ret;
    int i;
    struct umass_dev_info *dev = g_umass_dev_array;
    struct umass_softc *sc = NULL;

    while(1) {
        for (i = 0; i < MAX_DEVICE; i++) {
            umass_dev_lock(i);
            if (dev[i].used == 1) {
                sc = dev[i].sc;
                ret = umass_dev_is_ready(sc);
                if (ret == 0) {
                    if (dev[i].attached == 1) {
                        umass_detach_dev_sub(sc, dev[i].dev_unit, 1);
                        dev[i].attached = 0;
                    }
                    umass_dev_unlock(i);
                    continue;
                }

                if (dev[i].attached == 1) {
                    umass_dev_unlock(i);
                    continue;
                }

                ret = umass_attach_dev_sub(sc, dev[i].dev_unit);
                if (ret< 0) {
                    umass_dev_unlock(i);
                    PRINT_ERR("umass attach device sub failed!\n");
                    continue;
                }
            }
            umass_dev_unlock(i);
        }
        (void)sleep(1);
    }
}

int umass_dev_is_attached(unsigned int dev_unit)
{
    if (dev_unit >= MAX_DEVICE) {
        PRINT_ERR("%s %d, The device unit is wrong!\n", __FUNCTION__, __LINE__);
        return -1;
    }

    return g_umass_dev_array[dev_unit].attached;
}

static void umass_dev_add(struct umass_softc *sc, int dev_unit)
{
    int id = dev_unit;

    if (g_umass_dev_array[id].used == 1) {
        PRINT_ERR("The id of umass device array is used!, id=%d\n", dev_unit);
        return;
    }

    g_umass_dev_array[id].sc = sc;
    g_umass_dev_array[id].dev_unit = dev_unit;
    g_umass_dev_array[id].used = 1;
    umass_dev_mtx_init(id, MTX_DEF | MTX_RECURSE);
}

static void umass_dev_delete(struct umass_softc *sc, unsigned int dev_unit)
{
    unsigned int id = dev_unit;

    if (g_umass_dev_array[id].used == 0) {
        PRINT_ERR("The id of umass device array is not used!\n");
        return;
    }

    if (g_umass_dev_array[id].dev_unit == dev_unit &&
        g_umass_dev_array[id].sc == sc) {
        g_umass_dev_array[id].used = 0;
        g_umass_dev_array[id].sc = NULL;
        g_umass_dev_array[id].attached = 0;
        umass_dev_mtx_destroy(id);
    }
    else {
        PRINT_ERR("Can not find the umass device!\n");
    }
}

static void umass_dev_attach_flag_set(int dev_unit)
{
    g_umass_dev_array[dev_unit].attached = 1;
}

static void umass_task_check(int flag)
{
    int i;
    int ret;

    for (i = 0; i < MAX_DEVICE; i++) {
        if (g_umass_dev_array[i].used)
            break;
    }

    if (i == MAX_DEVICE) {
        if (flag == 0) {//create task
            ret = usb_os_task_creat(&umass_taskid, (TSK_ENTRY_FUNC)umass_dev_status_check,
                LOSCFG_BASE_CORE_TSK_DEFAULT_PRIO, "umass_task", 0);
            if (ret) {
                PRINT_ERR("Create umass task fail!\n");
                return;
            }
        }
        else if (flag == 1) { //delete task
            ret = usb_os_task_delete(umass_taskid);
            if (ret) {
                PRINT_ERR("Delete umass task fail!\n");
                return;
            }
        }
        else {
            PRINT_ERR("%s flag error!\n", __FUNCTION__);
        }
    }
}
#endif

static void devunit_to_devname(unsigned int dev_unit, char *devname)
{
    char name_suf;
    int ret;

#if USB_HAVE_DEVICE_TOPOLOGY
    if (!(0x1 & (dev_quantity >> dev_unit)))
#else
    if (dev_unit >= MAX_DEVICE)
#endif
    {
        dprintf("sorry, we don't support so many devices\n");
        return;
    }
    ret = strncpy_s(devname, strlen(UMASS_ATTACH_PRENAME) + 1, UMASS_ATTACH_PRENAME, strlen(UMASS_ATTACH_PRENAME));
    if (ret != EOK) {
        usb_err("strncpy_s failed!, ret:%d\n", ret);
        return;
    }
    name_suf = 'a' + dev_unit;
    devname[strlen(UMASS_ATTACH_PRENAME)] = name_suf;
    devname[strlen(UMASS_ATTACH_PRENAME) +1] = '\0';
}
static int32_t umass_attach_dev(struct umass_softc *sc, unsigned int dev_unit)
{
    int ret = 0;

    if (dev_unit >= MAX_DEVICE) {
        PRINT_ERR("sorry, we don't support so many devices\n");
        return -1;
    }
#if USB_SUPPORT_SD_HOT_PLUG
    umass_task_check(0);
    umass_dev_add(sc, dev_unit);
#endif

    ret = umass_dev_is_ready(sc);
    if (ret) {
        ret = umass_attach_dev_sub(sc, dev_unit);
        if (ret < 0) {
#if USB_SUPPORT_SD_HOT_PLUG
            umass_dev_delete(sc, dev_unit);
#endif
            PRINT_ERR("umass attach device fail!\n");
            return -1;
        }
    }

    return 0;
}

#if USB_HAVE_DEVICE_TOPOLOGY
int umass_medium_probe(uint8_t medium, char *devname)
{
    struct usbd_btNode *node = NULL;
    uint8_t dev_unit;

    if ((devname == NULL) || strlen(devname) > 7) { /* /dev/sd* */
        return -1;
    }

    if (medium < 1 || medium > usbd_get_hub_quantity()) {
        return -1;
    }

    node = usbd_perOrder_probe(hub_tree, "umass", &medium);
    if (node != NULL) {
        dev_unit = usbd_atoi(node->info.nameunit + 5); /* 5 = umass */
        devunit_to_devname(dev_unit, devname);
    } else {
        return -1;
    }
    return 0;
}
#endif

